Cut SkQP 2019-12-04

No-Try: true
Change-Id: Iaea6298d656e412ad24926a3cee6fb880a68e762
diff --git a/AUTHORS b/AUTHORS
old mode 100644
new mode 100755
index 80776a6..c7641d2
--- a/AUTHORS
+++ b/AUTHORS
@@ -16,7 +16,9 @@
 Amazon, Inc <*@amazon.com>
 Anthony Catel <paraboul@gmail.com>
 ARM <*@arm.com>
+Bharat Ahuja <ahujabharat93@gmail.com>
 Dawson Coleman <dawsonmcoleman@gmail.com>
+Deepak Mohan <hop2deep@gmail.com>
 Ehsan Akhgari <ehsan.akhgari@gmail.com>
 Facebook, Inc. <*fb.com>
 George Wright <george@mozilla.com>
diff --git a/BUILD.gn b/BUILD.gn
index 6c715d4..bcbe6eb 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -6,9 +6,13 @@
 import("gn/flutter_defines.gni")
 import("gn/fuchsia_defines.gni")
 import("gn/shared_sources.gni")
-
 import("gn/skia.gni")
 
+if (is_fuchsia) {
+  import("//build/fuchsia/sdk.gni")
+  import("build/fuchsia/fuchsia_download_sdk.gni")
+}
+
 if (skia_use_dawn) {
   import("third_party/externals/dawn/scripts/dawn_features.gni")
 }
@@ -207,6 +211,9 @@
       if (defined(invoker.public_configs)) {
         configs = invoker.public_configs
       }
+      if (defined(invoker.public_include_dirs)) {
+        include_dirs = invoker.public_include_dirs
+      }
     }
     source_set(target_name) {
       check_includes = false
@@ -317,9 +324,11 @@
   deps = []
 
   if (is_fuchsia && using_fuchsia_sdk) {
-    deps += [ "$fuchsia_sdk_root/fidl:fuchsia.fonts" ]
+    deps += [ "//build/fuchsia/fidl:fuchsia.fonts" ]
   } else {
-    deps += [ "//sdk/fidl/fuchsia.fonts" ]
+    deps = [
+      "//sdk/fidl/fuchsia.fonts",
+    ]
   }
   sources = [
     "src/ports/SkFontMgr_fuchsia.cpp",
@@ -500,7 +509,7 @@
   if (skia_generate_workarounds) {
     deps += [ ":workaround_list" ]
   }
-  public_defines = [ "SK_GL" ]
+  public_defines = []
   public_configs = []
   public_deps = []
 
@@ -515,9 +524,6 @@
     sources += [ "src/gpu/GrPathRendering_none.cpp" ]
   }
 
-  # These paths need to be absolute to match the ones produced by shared_sources.gni.
-  sources -= get_path_info([ "src/gpu/gl/GrGLMakeNativeInterface_none.cpp" ],
-                           "abspath")
   libs = []
   if (is_android) {
     sources += [ "src/gpu/gl/egl/GrGLMakeNativeInterface_egl.cpp" ]
@@ -545,6 +551,10 @@
     sources += [ "src/gpu/gl/GrGLMakeNativeInterface_none.cpp" ]
   }
 
+  if (skia_use_gl) {
+    public_defines += [ "SK_GL" ]
+    sources += skia_gl_gpu_sources
+  }
   if (skia_use_vulkan) {
     public_defines += [ "SK_VULKAN" ]
     deps += [ "third_party/vulkanmemoryallocator" ]
@@ -586,6 +596,7 @@
     public_defines += [ "SK_METAL" ]
     sources += skia_metal_sources
     libs += [ "Metal.framework" ]
+    libs += [ "MetalKit.framework" ]
     libs += [ "Foundation.framework" ]
     cflags_objcc += [ "-fobjc-arc" ]
   }
@@ -600,11 +611,23 @@
 }
 
 optional("gif") {
-  enabled = !skia_use_wuffs
-  sources = [
-    "src/codec/SkGifCodec.cpp",
-    "third_party/gif/SkGifImageReader.cpp",
-  ]
+  enabled = !skia_use_wuffs && skia_use_libgifcodec
+  _libgifcodec_gni_path = "third_party/externals/libgifcodec/libgifcodec.gni"
+  if ("True" ==
+      exec_script("gn/checkpath.py",
+                  [ rebase_path(_libgifcodec_gni_path, root_build_dir) ],
+                  "trim string")) {
+    public_defines = [ "SK_USE_LIBGIFCODEC" ]
+    public_include_dirs = [
+      ".",
+      skia_libgifcodec_path,
+    ]
+    include_dirs = public_include_dirs
+    import(_libgifcodec_gni_path)
+    sources = rebase_path(libgifcodec_sources + libgifcodec_public,
+                          ".",
+                          skia_libgifcodec_path)
+  }
 }
 
 optional("heif") {
@@ -766,9 +789,18 @@
   public_defines = [ "SK_ENABLE_SKSL_INTERPRETER" ]
 }
 
+config("vtune_includes") {
+  include_dirs = [ "$skia_vtune_path/include" ]
+}
 optional("skvm_jit") {
   enabled = skia_enable_skvm_jit
   public_defines = [ "SKVM_JIT" ]
+
+  if (skia_vtune_path != "") {
+    public_defines += [ "SKVM_JIT_VTUNE" ]
+    public_configs = [ ":vtune_includes" ]
+    libs = [ "$skia_vtune_path/lib64/libjitprofiling.a" ]
+  }
 }
 
 if (skia_enable_gpu && skia_generate_workarounds) {
@@ -892,7 +924,6 @@
     "src/ports/SkOSFile_stdio.cpp",
     "src/sfnt/SkOTTable_name.cpp",
     "src/sfnt/SkOTUtils.cpp",
-    "src/utils/mac/SkStream_mac.cpp",
   ]
 
   defines = []
@@ -1049,6 +1080,7 @@
     "src/core/SkEdgeClipper.cpp",
     "src/core/SkGeometry.cpp",
     "src/core/SkLineClipper.cpp",
+    "src/core/SkMalloc.cpp",
     "src/core/SkMallocPixelRef.cpp",
     "src/core/SkMath.cpp",
     "src/core/SkMatrix.cpp",
@@ -1165,8 +1197,6 @@
   }
 
   config("our_vulkan_headers") {
-    # We add this directory to simulate the client already have
-    # vulkan/vulkan_core.h on their path.
     include_dirs = [ "include/third_party/vulkan" ]
   }
 
@@ -1290,13 +1320,20 @@
       "tools/gpu/MemoryCache.h",
       "tools/gpu/ProxyUtils.cpp",
       "tools/gpu/TestContext.cpp",
+      "tools/gpu/TestOps.cpp",
+      "tools/gpu/TestOps.h",
       "tools/gpu/YUVUtils.cpp",
       "tools/gpu/YUVUtils.h",
-      "tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp",
-      "tools/gpu/gl/GLTestContext.cpp",
-      "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp",
       "tools/gpu/mock/MockTestContext.cpp",
     ]
+    if (skia_use_gl) {
+      sources += [
+        "tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp",
+        "tools/gpu/gl/GLTestContext.cpp",
+        "tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp",
+      ]
+    }
+
     libs = []
 
     if (is_android || skia_use_egl) {
@@ -1318,6 +1355,9 @@
       if (target_cpu != "arm64") {
         libs += [ "OpenGL32.lib" ]
       }
+    } else if (is_fuchsia && using_fuchsia_sdk) {
+      libs +=
+          [ "${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/lib/libzircon.so" ]
     }
 
     cflags_objcc = [ "-fobjc-arc" ]
@@ -1338,7 +1378,7 @@
       public_deps += [ "//third_party/dawn:dawn_headers" ]
       sources += [ "tools/gpu/dawn/DawnTestContext.cpp" ]
     }
-  }
+  }  # test_lib("gpu_tool_utils")
 
   test_lib("flags") {
     sources = [
@@ -1486,6 +1526,9 @@
   import("gn/gm.gni")
   test_lib("gm") {
     sources = gm_sources
+    if (skia_use_gl) {
+      sources += gl_gm_sources
+    }
     deps = [
       ":etc1",
       ":flags",
@@ -1522,6 +1565,10 @@
     sources = tests_sources + pathops_tests_sources
     if (skia_use_metal) {
       sources += metal_tests_sources
+      cflags_objcc = [ "-fobjc-arc" ]
+    }
+    if (skia_use_gl) {
+      sources += gl_tests_sources
     }
     if (!skia_enable_fontmgr_android) {
       sources -= [ "//tests/FontMgrAndroidParserTest.cpp" ]
@@ -1709,6 +1756,7 @@
     }
     test_app("fm") {
       sources = [
+        "dm/DMGpuTestProcs.cpp",  # blech
         "tools/fm/fm.cpp",
       ]
       deps = [
@@ -1720,6 +1768,7 @@
         ":gpu_tool_utils",
         ":hash_and_encode",
         ":skia",
+        ":tests",
         ":tool_utils",
         ":trace",
         "modules/skottie",
@@ -2008,7 +2057,13 @@
   }
 
   if (!is_win) {
-    test_lib("skqp_lib") {
+    source_set("skqp_lib") {
+      check_includes = false
+      testonly = true
+      if (!is_official_build) {
+        configs -= [ "//gn:warnings" ]
+      }
+      public_configs = [ ":skia_private" ]
       defines =
           [ "SK_SKQP_GLOBAL_ERROR_TOLERANCE=$skia_skqp_global_error_tolerance" ]
       sources = [
@@ -2018,7 +2073,6 @@
       ]
       deps = [
         ":gm",
-        ":gpu_tool_utils",
         ":skia",
         ":tests",
         ":tool_utils",
@@ -2028,10 +2082,10 @@
       sources = [
         "tools/skqp/src/skqp_main.cpp",
       ]
+      include_dirs = [ "//" ]
+      lib_dirs = []
       deps = [
-        ":skia",
         ":skqp_lib",
-        ":tool_utils",
       ]
     }
     test_app("jitter_gms") {
@@ -2045,9 +2099,22 @@
       ]
     }
   }
+  if (is_fuchsia) {
+    # Build a package repository for skqp on Fuchsia.
+    group("skqp_repo") {
+      testonly = true
+      deps = [
+        "//build/fuchsia/skqp:skqp_repo",
+      ]
+    }
+  }
   if (is_android) {
-    test_app("skqp_app") {
-      is_shared_library = true
+    shared_library("libskqp_app") {
+      configs += [ ":skia_private" ]
+      if (!is_official_build) {
+        configs -= [ "//gn:warnings" ]
+      }
+      testonly = true
       sources = [
         "tools/skqp/src/jni_skqp.cpp",
       ]
@@ -2295,7 +2362,8 @@
     }
   }
 
-  if (is_linux || is_mac || is_ios) {
+  if ((is_linux || is_mac || is_ios) && target_cpu != "mips64el" &&
+      target_cpu != "loongson3a") {
     test_app("SkiaSDLExample") {
       sources = [
         "example/SkiaSDLExample.cpp",
diff --git a/DEPS b/DEPS
index c510e4d..a407da8 100644
--- a/DEPS
+++ b/DEPS
@@ -7,15 +7,16 @@
 deps = {
   "buildtools"                            : "https://chromium.googlesource.com/chromium/buildtools.git@505de88083136eefd056e5ee4ca0f01fe9b33de8",
   "common"                                : "https://skia.googlesource.com/common.git@9737551d7a52c3db3262db5856e6bcd62c462b92",
-  "third_party/externals/angle2"          : "https://chromium.googlesource.com/angle/angle.git@77fba58c9658dcce5729f1df0217fe9e4c0a2733",
-  "third_party/externals/dawn"            : "https://dawn.googlesource.com/dawn.git@f104ec23bc569b5383b9d3df173beeef2b1ff128",
+  "third_party/externals/angle2"          : "https://chromium.googlesource.com/angle/angle.git@fb40d231c3e2ee7c38f8445ef5defc0ab0f5f15d",
+  "third_party/externals/dawn"            : "https://dawn.googlesource.com/dawn.git@3c086a0c2e1dc3e2e14aaa3d78c052c7e07274b4",
   "third_party/externals/dng_sdk"         : "https://android.googlesource.com/platform/external/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
   "third_party/externals/egl-registry"    : "https://skia.googlesource.com/external/github.com/KhronosGroup/EGL-Registry@a0bca08de07c7d7651047bedc0b653cfaaa4f2ae",
   "third_party/externals/expat"           : "https://android.googlesource.com/platform/external/expat.git@e5aa0a2cb0a5f759ef31c0819dc67d9b14246a4a",
-  "third_party/externals/freetype"        : "https://skia.googlesource.com/third_party/freetype2.git@05439f5cc69eaa3deaf3db52a7999af09a2c293a",
+  "third_party/externals/freetype"        : "https://skia.googlesource.com/third_party/freetype2.git@0a3d2bb99b45b72e1d45185ab054efa993d97210",
   "third_party/externals/harfbuzz"        : "https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@1bada656a86e9cb27d4a6b9fcc50748f0bd9c1d9",
   "third_party/externals/icu"             : "https://chromium.googlesource.com/chromium/deps/icu.git@407b39301e71006b68bd38e770f35d32398a7b14",
   "third_party/externals/imgui"           : "https://skia.googlesource.com/external/github.com/ocornut/imgui.git@d38d7c6628bebd02692cfdd6fa76b4d992a35b75",
+  "third_party/externals/libgifcodec"     : "https://skia.googlesource.com/libgifcodec@ed218be1ba3de964c186ceac8e1e344853530edf",
   "third_party/externals/libjpeg-turbo"   : "https://skia.googlesource.com/external/github.com/libjpeg-turbo/libjpeg-turbo.git@574f3a772c96dc9db2c98ef24706feb3f6dbda9a",
   "third_party/externals/libpng"          : "https://skia.googlesource.com/third_party/libpng.git@386707c6d19b974ca2e3db7f5c61873813c6fe44",
   "third_party/externals/libwebp"         : "https://chromium.googlesource.com/webm/libwebp.git@0fe1a89dbf1930fc2554dbe76adad5d962054ead",
@@ -30,13 +31,13 @@
   "third_party/externals/spirv-cross"     : "https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Cross@53ab2144b90abede33be5161aec5dfc94ddc3caf",
   "third_party/externals/spirv-headers"   : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@29c11140baaf9f7fdaa39a583672c556bf1795a1",
   "third_party/externals/spirv-tools"     : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@0c4feb643b89d1792b02f7cbef315e9d95633bd7",
-  "third_party/externals/swiftshader"     : "https://swiftshader.googlesource.com/SwiftShader@6d69aae0e1ab49190ea46cd1c999fd3d02e016b9",
+  "third_party/externals/swiftshader"     : "https://swiftshader.googlesource.com/SwiftShader@b64fbfec4dcd3f81c776198641b1fc71ac8d64a4",
   #"third_party/externals/v8"              : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
   "third_party/externals/wuffs"           : "https://skia.googlesource.com/external/github.com/google/wuffs.git@4080840928c0b05a80cda0d14ac2e2615f679f1a",
   "third_party/externals/zlib"            : "https://chromium.googlesource.com/chromium/src/third_party/zlib@47af7c547f8551bd25424e56354a2ae1e9062859",
 
   "../src": {
-    "url": "https://chromium.googlesource.com/chromium/src.git@7ca6de3adb8e5c252e6a35ecd71bb0f52e4e559b",
+    "url": "https://chromium.googlesource.com/chromium/src.git@8a4500482e8d3905009d8bdc330fa9abc8306073",
     "condition": "checkout_chromium",
   },
 }
diff --git a/LICENSE b/LICENSE
index fc53e3e..e32636d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,29 +1,29 @@
-// Copyright (c) 2011 Google Inc. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011 Google Inc. All rights reserved.
 
---------------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+  * Neither the name of the copyright holder nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index af2be25..b879c6c 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -7,6 +7,30 @@
 Milestone 80
 
 <Insert new notes here- top is most recent.>
+  * Added SkMatrix::MakeTrans(SkVector) and SkRect::makeOffset(SkVector).
+    https://review.skia.org/255782
+
+  * Added SkImageInfo::MakeA8(SkISize) and added optional color space parameter to
+    SkImageInfo::MakeN32Premul(SkISize).
+
+  * Added dimensions() and getFrameCount() to SkAnimatedImage
+    https://review.skia.org/253542
+
+  * Removed SkMatrix44 version of toXYZD50 from SkColorSpace. Switched to skcms types in
+    transferFn, invTrasnferFn, and gamutTransformTo functions.
+    https://review.skia.org/252596
+
+  * Removed rotation and YUV support from SkColorMatrix
+    https://review.skia.org/252188
+
+  * Added kBT2020_SkYUVColorSpace. This is BT.2020's YCbCr conversion (non-constant-luminance).
+    https://review.skia.org/252160
+
+  * Remove old async read pixels APIs
+    https://review.skia.org/251198
+
+  * Expose SkBlendModeCoeff and SkBlendMode_AsCoeff for Porter-Duff blend modes.
+    https://review.skia.org/252600
 
 * * *
 
diff --git a/bench/AAClipBench.cpp b/bench/AAClipBench.cpp
index abd7ec5..57af3be 100644
--- a/bench/AAClipBench.cpp
+++ b/bench/AAClipBench.cpp
@@ -219,7 +219,7 @@
         path.addCircle(0, 0, SkIntToScalar(200));
         path.addCircle(0, 0, SkIntToScalar(180));
         // evenodd means we've constructed basically a stroked circle
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
 
         SkIRect bounds;
         path.getBounds().roundOut(&bounds);
diff --git a/bench/BlurRectsBench.cpp b/bench/BlurRectsBench.cpp
index ed2b254..df1775c 100644
--- a/bench/BlurRectsBench.cpp
+++ b/bench/BlurRectsBench.cpp
@@ -34,8 +34,8 @@
         paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, fRadius));
 
         SkPath path;
-        path.addRect(fOuter, SkPath::kCW_Direction);
-        path.addRect(fInner, SkPath::kCW_Direction);
+        path.addRect(fOuter, SkPathDirection::kCW);
+        path.addRect(fInner, SkPathDirection::kCW);
 
         for (int i = 0; i < loops; i++) {
             canvas->drawPath(path, paint);
diff --git a/bench/BulkRectBench.cpp b/bench/BulkRectBench.cpp
new file mode 100644
index 0000000..c4a401f
--- /dev/null
+++ b/bench/BulkRectBench.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "bench/Benchmark.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkImage.h"
+#include "include/core/SkPaint.h"
+#include "include/gpu/GrContext.h"
+#include "include/utils/SkRandom.h"
+
+#include "src/gpu/GrClip.h"
+#include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/SkGr.h"
+
+// Benchmarks that exercise the bulk image and solid color quad APIs, under a variety of patterns:
+enum class ImageMode {
+    kShared, // 1. One shared image referenced by every rectangle
+    kUnique, // 2. Unique image for every rectangle
+    kNone    // 3. No image, solid color shading per rectangle
+};
+//   X
+enum class DrawMode {
+    kBatch,  // Bulk API submission, one call to draw every rectangle
+    kRef,    // One standard SkCanvas draw call per rectangle
+    kQuad    // One experimental draw call per rectangle, only for solid color draws
+};
+//   X
+enum class RectangleLayout {
+    kRandom,  // Random overlapping rectangles
+    kGrid     // Small, non-overlapping rectangles in a grid covering the output surface
+};
+
+// Benchmark runner that can be configured by template arguments.
+template<int kRectCount, RectangleLayout kLayout, ImageMode kImageMode, DrawMode kDrawMode>
+class BulkRectBench : public Benchmark {
+public:
+    static_assert(kImageMode == ImageMode::kNone || kDrawMode != DrawMode::kQuad,
+                  "kQuad only supported for solid color draws");
+
+    static constexpr int kWidth      = 1024;
+    static constexpr int kHeight     = 1024;
+
+    // There will either be 0 images, 1 image, or 1 image per rect
+    static constexpr int kImageCount = kImageMode == ImageMode::kShared ?
+            1 : (kImageMode == ImageMode::kNone ? 0 : kRectCount);
+
+    bool isSuitableFor(Backend backend) override {
+        if (kDrawMode == DrawMode::kBatch && kImageMode == ImageMode::kNone) {
+            // Currently the bulk color quad API is only available on GrRenderTargetContext
+            return backend == kGPU_Backend;
+        } else {
+            return this->INHERITED::isSuitableFor(backend);
+        }
+    }
+
+protected:
+    SkRect         fRects[kRectCount];
+    sk_sp<SkImage> fImages[kImageCount];
+    SkColor4f      fColors[kRectCount];
+    SkString       fName;
+
+    void computeName()  {
+        fName = "bulkrect";
+        fName.appendf("_%d", kRectCount);
+        if (kLayout == RectangleLayout::kRandom) {
+            fName.append("_random");
+        } else {
+            fName.append("_grid");
+        }
+        if (kImageMode == ImageMode::kShared) {
+            fName.append("_sharedimage");
+        } else if (kImageMode == ImageMode::kUnique) {
+            fName.append("_uniqueimages");
+        } else {
+            fName.append("_solidcolor");
+        }
+        if (kDrawMode == DrawMode::kBatch) {
+            fName.append("_batch");
+        } else if (kDrawMode == DrawMode::kRef) {
+            fName.append("_ref");
+        } else {
+            fName.append("_quad");
+        }
+    }
+
+    void drawImagesBatch(SkCanvas* canvas) const {
+        SkASSERT(kImageMode != ImageMode::kNone);
+        SkASSERT(kDrawMode == DrawMode::kBatch);
+
+        SkCanvas::ImageSetEntry batch[kRectCount];
+        for (int i = 0; i < kRectCount; ++i) {
+            int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
+            batch[i].fImage = fImages[imageIndex];
+            batch[i].fSrcRect = SkRect::MakeIWH(fImages[imageIndex]->width(),
+                                                fImages[imageIndex]->height());
+            batch[i].fDstRect = fRects[i];
+            batch[i].fAAFlags = SkCanvas::kAll_QuadAAFlags;
+        }
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setFilterQuality(kLow_SkFilterQuality);
+
+        canvas->experimental_DrawEdgeAAImageSet(batch, kRectCount, nullptr, nullptr, &paint,
+                                                SkCanvas::kFast_SrcRectConstraint);
+    }
+
+    void drawImagesRef(SkCanvas* canvas) const {
+        SkASSERT(kImageMode != ImageMode::kNone);
+        SkASSERT(kDrawMode == DrawMode::kRef);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setFilterQuality(kLow_SkFilterQuality);
+
+        for (int i = 0; i < kRectCount; ++i) {
+            int imageIndex = kImageMode == ImageMode::kShared ? 0 : i;
+            SkIRect srcRect = SkIRect::MakeWH(fImages[imageIndex]->width(),
+                                              fImages[imageIndex]->height());
+            canvas->drawImageRect(fImages[imageIndex].get(), srcRect, fRects[i], &paint,
+                                  SkCanvas::kFast_SrcRectConstraint);
+        }
+    }
+
+    void drawSolidColorsBatch(SkCanvas* canvas) const {
+        SkASSERT(kImageMode == ImageMode::kNone);
+        SkASSERT(kDrawMode == DrawMode::kBatch);
+
+        GrContext* context = canvas->getGrContext();
+        SkASSERT(context);
+
+        GrRenderTargetContext::QuadSetEntry batch[kRectCount];
+        for (int i = 0; i < kRectCount; ++i) {
+            batch[i].fRect = fRects[i];
+            batch[i].fColor = fColors[i].premul();
+            batch[i].fLocalMatrix = SkMatrix::I();
+            batch[i].fAAFlags = GrQuadAAFlags::kAll;
+        }
+
+        SkPaint paint;
+        paint.setColor(SK_ColorWHITE);
+        paint.setAntiAlias(true);
+
+        GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
+        SkMatrix view = canvas->getTotalMatrix();
+        GrPaint grPaint;
+        SkPaintToGrPaint(context, rtc->colorInfo(), paint, view, &grPaint);
+        rtc->drawQuadSet(GrNoClip(), std::move(grPaint), GrAA::kYes, view, batch, kRectCount);
+    }
+
+    void drawSolidColorsRef(SkCanvas* canvas) const {
+        SkASSERT(kImageMode == ImageMode::kNone);
+        SkASSERT(kDrawMode == DrawMode::kRef || kDrawMode == DrawMode::kQuad);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        for (int i = 0; i < kRectCount; ++i) {
+            if (kDrawMode == DrawMode::kRef) {
+                paint.setColor4f(fColors[i]);
+                canvas->drawRect(fRects[i], paint);
+            } else {
+                canvas->experimental_DrawEdgeAAQuad(fRects[i], nullptr, SkCanvas::kAll_QuadAAFlags,
+                                                    fColors[i], SkBlendMode::kSrcOver);
+            }
+        }
+    }
+
+    const char* onGetName() override {
+        if (fName.isEmpty()) {
+            this->computeName();
+        }
+        return fName.c_str();
+    }
+
+    void onDelayedSetup() override {
+        static constexpr SkScalar kMinRectSize = 0.2f;
+        static constexpr SkScalar kMaxRectSize = 300.f;
+
+        SkRandom rand;
+        for (int i = 0; i < kRectCount; i++) {
+            if (kLayout == RectangleLayout::kRandom) {
+                SkScalar w = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize;
+                SkScalar h = rand.nextF() * (kMaxRectSize - kMinRectSize) + kMinRectSize;
+
+                SkScalar x = rand.nextF() * (kWidth - w);
+                SkScalar y = rand.nextF() * (kHeight - h);
+
+                fRects[i].setXYWH(x, y, w, h);
+            } else {
+                int gridSize = SkScalarCeilToInt(SkScalarSqrt(kRectCount));
+                SkASSERT(gridSize * gridSize >= kRectCount);
+
+                SkScalar w = (kWidth - 1.f) / gridSize;
+                SkScalar h = (kHeight - 1.f) / gridSize;
+
+                SkScalar x = (i % gridSize) * w + 0.5f; // Offset to ensure AA doesn't get disabled
+                SkScalar y = (i / gridSize) * h + 0.5f;
+
+                fRects[i].setXYWH(x, y, w, h);
+            }
+
+            // Make sure we don't extend outside the render target, don't want to include clipping
+            // in the benchmark.
+            SkASSERT(SkRect::MakeWH(kWidth, kHeight).contains(fRects[i]));
+
+            fColors[i] = {rand.nextF(), rand.nextF(), rand.nextF(), 1.f};
+        }
+    }
+
+    void onPerCanvasPreDraw(SkCanvas* canvas) override {
+        // Push the skimages to the GPU when using the GPU backend so that the texture creation is
+        // not part of the bench measurements. Always remake the images since they are so simple,
+        // and since they are context-specific, this works when the bench runs multiple GPU backends
+        GrContext* context = canvas->getGrContext();
+        for (int i = 0; i < kImageCount; ++i) {
+            SkBitmap bm;
+            bm.allocN32Pixels(256, 256);
+            bm.eraseColor(fColors[i].toSkColor());
+            auto image = SkImage::MakeFromBitmap(bm);
+
+            fImages[i] = context ? image->makeTextureImage(context) : std::move(image);
+        }
+    }
+
+    void onDraw(int loops, SkCanvas* canvas) override {
+        for (int i = 0; i < loops; i++) {
+            if (kImageMode == ImageMode::kNone) {
+                if (kDrawMode == DrawMode::kBatch) {
+                    this->drawSolidColorsBatch(canvas);
+                } else {
+                    this->drawSolidColorsRef(canvas);
+                }
+            } else {
+                if (kDrawMode == DrawMode::kBatch) {
+                    this->drawImagesBatch(canvas);
+                } else {
+                    this->drawImagesRef(canvas);
+                }
+            }
+        }
+    }
+
+    SkIPoint onGetSize() override {
+        return { kWidth, kHeight };
+    }
+
+    typedef Benchmark INHERITED;
+};
+
+// constructor call is wrapped in () so the macro doesn't break parsing the commas in the template
+#define ADD_BENCH(n, layout, imageMode, drawMode)                              \
+    DEF_BENCH( return (new BulkRectBench<n, layout, imageMode, drawMode>()); )
+
+#define ADD_BENCH_FAMILY(n, layout)                                            \
+    ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kBatch)                 \
+    ADD_BENCH(n, layout, ImageMode::kShared, DrawMode::kRef)                   \
+    ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kBatch)                 \
+    ADD_BENCH(n, layout, ImageMode::kUnique, DrawMode::kRef)                   \
+    ADD_BENCH(n, layout, ImageMode::kNone,   DrawMode::kBatch)                 \
+    ADD_BENCH(n, layout, ImageMode::kNone,   DrawMode::kRef)                   \
+    ADD_BENCH(n, layout, ImageMode::kNone,   DrawMode::kQuad)
+
+ADD_BENCH_FAMILY(1000,  RectangleLayout::kRandom)
+ADD_BENCH_FAMILY(1000,  RectangleLayout::kGrid)
+
+#undef ADD_BENCH_FAMILY
+#undef ADD_BENCH
diff --git a/bench/GeometryBench.cpp b/bench/GeometryBench.cpp
index 457151d..abaf678 100644
--- a/bench/GeometryBench.cpp
+++ b/bench/GeometryBench.cpp
@@ -271,7 +271,7 @@
 
     void onDraw(int loops, SkCanvas* canvas) override {
         for (int i = 0; i < loops; ++i) {
-            fPath.setConvexity(SkPath::kUnknown_Convexity);
+            fPath.setConvexityType(SkPathConvexityType::kUnknown);
             (void)fPath.isConvex();
         }
     }
diff --git a/bench/ImageCycleBench.cpp b/bench/ImageCycleBench.cpp
index 032e43b..1bb38d0 100644
--- a/bench/ImageCycleBench.cpp
+++ b/bench/ImageCycleBench.cpp
@@ -73,7 +73,9 @@
                 }
             }
             // Prevent any batching between "frames".
-            canvas->flush();
+            if (auto surf = canvas->getSurface()) {
+                surf->flush();
+            }
         }
     }
 
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index eb4d4a3..c92e88d 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -691,7 +691,7 @@
 
             // mimic how Chrome does circles
             temp.arcTo(r, 0, 0, false);
-            temp.addOval(r, SkPath::kCCW_Direction);
+            temp.addOval(r, SkPathDirection::kCCW);
             temp.arcTo(r, 360, 0, true);
             temp.close();
 
@@ -1206,7 +1206,7 @@
         fPath.addRRect(SkRRect::MakeRectXY(r, w/8.0f, h/8.0f));
 
         if (forceConcave) {
-            fPath.setConvexity(SkPath::kConcave_Convexity);
+            fPath.setConvexityType(SkPathConvexityType::kConcave);
             SkASSERT(!fPath.isConvex());
         } else {
             SkASSERT(fPath.isConvex());
diff --git a/bench/SKPAnimationBench.cpp b/bench/SKPAnimationBench.cpp
index ee25f19..8cb9d8d 100644
--- a/bench/SKPAnimationBench.cpp
+++ b/bench/SKPAnimationBench.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "bench/SKPAnimationBench.h"
-#include "include/core/SkMultiPictureDraw.h"
 #include "include/core/SkSurface.h"
 #include "tools/flags/CommandLineFlags.h"
 
diff --git a/bench/SKPBench.cpp b/bench/SKPBench.cpp
index 8543174..9fe11f8 100644
--- a/bench/SKPBench.cpp
+++ b/bench/SKPBench.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "bench/SKPBench.h"
-#include "include/core/SkMultiPictureDraw.h"
 #include "include/core/SkSurface.h"
 #include "tools/flags/CommandLineFlags.h"
 
@@ -125,17 +124,7 @@
 }
 
 void SKPBench::drawMPDPicture() {
-    SkMultiPictureDraw mpd;
-
-    for (int j = 0; j < fTileRects.count(); ++j) {
-        SkMatrix trans;
-        trans.setTranslate(-fTileRects[j].fLeft/fScale,
-                           -fTileRects[j].fTop/fScale);
-        mpd.add(fSurfaces[j]->getCanvas(), fPic.get(), &trans);
-    }
-
-    // We flush after each picture to more closely model how Chrome rasterizes tiles.
-    mpd.draw(/*flush = */ true);
+    // TODO: remove me
 }
 
 void SKPBench::drawPicture() {
diff --git a/bench/VertexColorSpaceBench.cpp b/bench/VertexColorSpaceBench.cpp
index 1dde32e..ddafc3d 100644
--- a/bench/VertexColorSpaceBench.cpp
+++ b/bench/VertexColorSpaceBench.cpp
@@ -35,25 +35,11 @@
 
 class GP : public GrGeometryProcessor {
 public:
-    GP(Mode mode, sk_sp<GrColorSpaceXform> colorSpaceXform)
-            : INHERITED(kVertexColorSpaceBenchGP_ClassID)
-            , fMode(mode)
-            , fColorSpaceXform(std::move(colorSpaceXform)) {
-        fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
-        switch (fMode) {
-            case kBaseline_Mode:
-            case kShader_Mode:
-                fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
-                break;
-            case kFloat_Mode:
-                fInColor = {"inColor", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
-                break;
-            case kHalf_Mode:
-                fInColor = {"inColor", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
-                break;
-        }
-        this->setVertexAttributes(&fInPosition, 2);
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, Mode mode,
+                                     sk_sp<GrColorSpaceXform> colorSpaceXform) {
+        return arena->make<GP>(mode, std::move(colorSpaceXform));
     }
+
     const char* name() const override { return "VertexColorXformGP"; }
 
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
@@ -93,7 +79,7 @@
             }
             void setData(const GrGLSLProgramDataManager& pdman,
                          const GrPrimitiveProcessor& primProc,
-                         FPCoordTransformIter&&) override {
+                         const CoordTransformRange&) override {
                 const GP& gp = primProc.cast<GP>();
                 fColorSpaceHelper.setData(pdman, gp.fColorSpaceXform.get());
             }
@@ -109,6 +95,28 @@
     }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
+    GP(Mode mode, sk_sp<GrColorSpaceXform> colorSpaceXform)
+            : INHERITED(kVertexColorSpaceBenchGP_ClassID)
+            , fMode(mode)
+            , fColorSpaceXform(std::move(colorSpaceXform)) {
+        fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
+        switch (fMode) {
+            case kBaseline_Mode:
+            case kShader_Mode:
+                fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
+                break;
+            case kFloat_Mode:
+                fInColor = {"inColor", kFloat4_GrVertexAttribType, kHalf4_GrSLType};
+                break;
+            case kHalf_Mode:
+                fInColor = {"inColor", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
+                break;
+        }
+        this->setVertexAttributes(&fInPosition, 2);
+    }
+
     Mode fMode;
     sk_sp<GrColorSpaceXform> fColorSpaceXform;
 
@@ -160,7 +168,7 @@
     friend class ::GrOpMemoryPool;
 
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp(new GP(fMode, fColorSpaceXform));
+        GrGeometryProcessor* gp = GP::Make(target->allocator(), fMode, fColorSpaceXform);
 
         size_t vertexStride = gp->vertexStride();
         const int kVertexCount = 1024;
@@ -223,7 +231,7 @@
         GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangleStrip);
         mesh->setNonIndexedNonInstanced(kVertexCount);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(gp, mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangleStrip);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
diff --git a/bin/try.py b/bin/try.py
index f86f2cd..3c72f24 100755
--- a/bin/try.py
+++ b/bin/try.py
@@ -18,8 +18,8 @@
 import tempfile
 
 
-BUCKET_SKIA_PRIMARY = 'skia.primary'
-BUCKET_SKIA_INTERNAL = 'skia.internal'
+BUCKET_SKIA_PRIMARY = 'skia/skia.primary'
+BUCKET_SKIA_INTERNAL = 'skia-internal/skia.internal'
 CHECKOUT_ROOT = os.path.realpath(os.path.join(
     os.path.dirname(os.path.abspath(__file__)), os.pardir))
 INFRA_BOTS = os.path.join(CHECKOUT_ROOT, 'infra', 'bots')
@@ -29,7 +29,6 @@
 
 sys.path.insert(0, INFRA_BOTS)
 
-import update_meta_config
 import utils
 
 
@@ -69,7 +68,6 @@
     jobs.append((BUCKET_SKIA_PRIMARY, json.load(f)))
   if args.internal:
     jobs.append(get_jobs(REPO_INTERNAL))
-  jobs.extend(update_meta_config.CQ_INCLUDE_CHROMIUM_TRYBOTS)
   if args.job:
     filtered_jobs = []
     for bucket, job_list in jobs:
diff --git a/build/fuchsia/BUILD.gn b/build/fuchsia/BUILD.gn
new file mode 100644
index 0000000..525dd70
--- /dev/null
+++ b/build/fuchsia/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+import("//build/fuchsia/sdk.gni")
+
+fuchsia_sdk_manifest_exists = false
+if (is_fuchsia && using_fuchsia_sdk) {
+  manifest_exists = exec_script("//build/fuchsia/file_exists",
+                                [
+                                  "-file_name",
+                                  rebase_path(fuchsia_sdk_manifest_path),
+                                ],
+                                "list lines",
+                                [ "//build/fuchsia/file_exists" ])
+  if (manifest_exists == [ "true" ]) {
+    fuchsia_sdk_manifest_exists = true
+  }
+}
+
+group("fuchsia") {
+  if (fuchsia_sdk_manifest_exists == true) {
+    deps = [
+      "fidl",
+      "pkg",
+      "sysroot",
+    ]
+  } else {
+    assert(false,
+           "Fuchsia SDK not found. Set arg skia_update_fuchsia_sdk=True " +
+               "to initialize.")
+  }
+}
diff --git a/build/fuchsia/fidl/BUILD.gn b/build/fuchsia/fidl/BUILD.gn
new file mode 100644
index 0000000..db7239b4
--- /dev/null
+++ b/build/fuchsia/fidl/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+#
+# Generate gn targets for fidl libraries in the Fuchsia SDK.
+#
+fuchsia_sdk("fidl") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [ "fidl_library" ]
+}
diff --git a/build/fuchsia/fidl_gen_cpp b/build/fuchsia/fidl_gen_cpp
new file mode 100755
index 0000000..6d2c10d
--- /dev/null
+++ b/build/fuchsia/fidl_gen_cpp
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Generate C/C++ headers and source files from the set of FIDL files
+ specified in the meta.json manifest.
+"""
+
+import argparse
+import collections
+import json
+import os
+import subprocess
+import sys
+
+def GetFIDLFilesRecursive(libraries, sdk_base, path):
+  with open(path) as json_file:
+    parsed = json.load(json_file)
+    result = []
+    deps = parsed['deps']
+    for dep in deps:
+      dep_meta_json = os.path.abspath('%s/fidl/%s/meta.json' % (sdk_base, dep))
+      GetFIDLFilesRecursive(libraries, sdk_base, dep_meta_json)
+    libraries[parsed['name']] = result + parsed['sources']
+
+def GetFIDLFilesByLibraryName(sdk_base, root):
+  libraries = collections.OrderedDict()
+  GetFIDLFilesRecursive(libraries, sdk_base, root)
+  return libraries
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--fidlc-bin', dest='fidlc_bin', action='store', required=True)
+  parser.add_argument('--fidlgen-bin', dest='fidlgen_bin', action='store', required=True)
+
+  parser.add_argument('--sdk-base', dest='sdk_base', action='store', required=True)
+  parser.add_argument('--root', dest='root', action='store', required=True)
+  parser.add_argument('--json', dest='json', action='store', required=True)
+  parser.add_argument('--include-base', dest='include_base', action='store', required=True)
+  parser.add_argument('--output-base-cc', dest='output_base_cc', action='store', required=True)
+  parser.add_argument('--output-c-header', dest='output_header_c', action='store', required=True)
+  parser.add_argument('--output-c-tables', dest='output_c_tables', action='store', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.fidlc_bin)
+  assert os.path.exists(args.fidlgen_bin)
+
+  fidl_files_by_name = GetFIDLFilesByLibraryName(args.sdk_base, args.root)
+
+  fidlc_command = [
+    args.fidlc_bin,
+    '--c-header',
+    args.output_header_c,
+    '--tables',
+    args.output_c_tables,
+    '--json',
+    args.json
+  ]
+
+  for _, fidl_files in fidl_files_by_name.iteritems():
+    fidlc_command.append('--files')
+    for fidl_file in fidl_files:
+      fidl_abspath = os.path.abspath('%s/%s' % (args.sdk_base, fidl_file))
+      fidlc_command.append(fidl_abspath)
+
+  subprocess.check_call(fidlc_command)
+
+  assert os.path.exists(args.json)
+
+  fidlgen_command = [
+    args.fidlgen_bin,
+    '-generators',
+    'cpp',
+    '-include-base',
+    args.include_base,
+    '-json',
+    args.json,
+    '-output-base',
+    args.output_base_cc
+  ]
+
+  subprocess.check_call(fidlgen_command)
+
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/file_exists b/build/fuchsia/file_exists
new file mode 100755
index 0000000..0d487af
--- /dev/null
+++ b/build/fuchsia/file_exists
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+  Prints "true" if the input |file_name| exists.
+"""
+
+import argparse
+import os
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-file_name", type=str,
+        help="File name for which to check existence for.")
+args = parser.parse_args()
+if os.path.exists(args.file_name):
+  print "true"
diff --git a/build/fuchsia/fuchsia_download_sdk.gni b/build/fuchsia/fuchsia_download_sdk.gni
new file mode 100644
index 0000000..8e63f1b
--- /dev/null
+++ b/build/fuchsia/fuchsia_download_sdk.gni
@@ -0,0 +1,18 @@
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import ("../../gn/skia.gni")
+import ("//build/fuchsia/sdk.gni")
+
+if(is_fuchsia && skia_update_fuchsia_sdk &&
+   current_toolchain == default_toolchain) {
+  cipd_dir = "${fuchsia_sdk_path}/../cipd"
+  update_sdk_out = exec_script("//build/fuchsia/update_fuchsia_sdk",
+              [ "-sdk_dir=" + rebase_path(fuchsia_sdk_path),
+                "-clang_dir=" + rebase_path(fuchsia_toolchain_path),
+                "-cipd_cache_dir=" + rebase_path(cipd_dir),
+                "-cipd_clang_version=git_revision:a6e1de4afc51560df18c95cb616dec51248ed660" ],
+              "list lines",
+              [ "//build/fuchsia/update_fuchsia_sdk" ])
+}
diff --git a/build/fuchsia/gen_package b/build/fuchsia/gen_package
new file mode 100755
index 0000000..407ee7e
--- /dev/null
+++ b/build/fuchsia/gen_package
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Builds a Fuchsia FAR archive.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--pm-bin', dest='pm_bin', action='store', required=True)
+  parser.add_argument(
+      '--pkg-dir', dest='pkg_dir', action='store', required=True)
+  parser.add_argument(
+      '--pkg-name', dest='pkg_name', action='store', required=True)
+  parser.add_argument(
+      '--pkg-version', dest='pkg_version', action='store', required=True)
+  parser.add_argument(
+      '--pkg-manifest', dest='pkg_manifest', action='store', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.pm_bin)
+  assert os.path.exists(args.pkg_dir)
+
+  pkg_dir = args.pkg_dir
+  pkg_name = args.pkg_name
+  pkg_manifest = args.pkg_manifest
+  pkg_version = args.pkg_version
+
+  pm_command_base = [
+      args.pm_bin,
+      '-o',
+      pkg_dir,
+  ]
+
+  # Create the package ID file.
+  subprocess.check_call(pm_command_base + ['-n'] + [pkg_name] + ['init'])
+
+  # Build the package.
+  subprocess.check_call(pm_command_base + ['-m'] + [pkg_manifest] + ['build'])
+
+  # Archive the package.
+  subprocess.check_call(pm_command_base + ['-m'] + [pkg_manifest] + ['-version'] + [pkg_version] + ['archive'])
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/gen_repo b/build/fuchsia/gen_repo
new file mode 100755
index 0000000..ed69ff0
--- /dev/null
+++ b/build/fuchsia/gen_repo
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+  Generate a Fuchsia repo capable of serving Fuchsia archives over the
+  network.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+def main():
+  parser = argparse.ArgumentParser()
+
+  parser.add_argument('--pm-bin', dest='pm_bin', action='store', required=True)
+  parser.add_argument(
+      '--repo-dir', dest='repo_dir', action='store', required=True)
+  parser.add_argument(
+      '--archive', dest='archives', action='append', required=True)
+
+  args = parser.parse_args()
+
+  assert os.path.exists(args.pm_bin)
+
+  if not os.path.exists(args.repo_dir):
+    pm_newrepo_command = [args.pm_bin, 'newrepo', '-repo', args.repo_dir]
+    subprocess.check_call(pm_newrepo_command)
+
+  pm_publish_command = [
+      args.pm_bin,
+      'publish',
+      '-C',  # Remove all previous registrations.
+      '-a',  # Publish archives from an archive (mode).
+      '-repo',
+      args.repo_dir
+  ]
+
+  for archive in args.archives:
+    pm_publish_command.append('-f')
+    pm_publish_command.append(archive)
+
+  print "PM Publish: "
+  print pm_publish_command
+  subprocess.check_call(pm_publish_command)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/build/fuchsia/pkg/BUILD.gn b/build/fuchsia/pkg/BUILD.gn
new file mode 100644
index 0000000..5d6b19f
--- /dev/null
+++ b/build/fuchsia/pkg/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+#
+# Generate ninja targets for cc_source_libraries and cc_prebuilt_libraries
+# in the SDK.  "pkg" is the parent directory within the SDK that contain
+# the "meta.json" files for each library that defines their build
+# dependencies.
+#
+fuchsia_sdk("pkg") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [
+    "cc_source_library",
+    "cc_prebuilt_library",
+  ]
+}
diff --git a/build/fuchsia/sdk.gni b/build/fuchsia/sdk.gni
new file mode 100644
index 0000000..0cefb8f
--- /dev/null
+++ b/build/fuchsia/sdk.gni
@@ -0,0 +1,406 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# import("//gn/skia.gni")
+
+declare_args() {
+  using_fuchsia_sdk = true
+
+  # Fuchsia SDK install dir.
+  fuchsia_sdk_path = "//fuchsia/sdk/$host_os"
+
+  # Clang install dir.
+  fuchsia_toolchain_path = "//fuchsia/toolchain/$host_os"
+
+  # Path to GN-generated GN targets derived from parsing json file at
+  # |fuchsia_sdk_manifest_path|.  The parsing logic can be found in sdk.gni.
+  fuchsia_sdk_root = "//build/fuchsia"
+}
+
+declare_args() {
+  fuchsia_sdk_manifest_path = "${fuchsia_sdk_path}/meta/manifest.json"
+}
+
+template("_fuchsia_sysroot") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(target_cpu == "x64" || target_cpu == "arm64",
+         "We currently only support 'x64' and 'arm64' targets for fuchsia.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "sysroot")
+
+  meta_json_versions = meta_json.versions
+  if (target_cpu == "x64") {
+    defs = meta_json_versions.x64
+  } else {
+    defs = meta_json_versions.arm64
+  }
+
+  _libs = []
+  _lib_dirs = []
+  _include_dirs = []
+
+  foreach(link_lib, defs.link_libs) {
+    if (link_lib != "arch/${target_cpu}/sysroot/lib/Scrt1.o") {
+      _libs += [ rebase_path("$fuchsia_sdk_path/$link_lib") ]
+    }
+  }
+
+  defs_include_dir = defs.include_dir
+  _include_dirs += [ rebase_path("$fuchsia_sdk_path/$defs_include_dir") ]
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    lib_dirs = _lib_dirs
+    libs = _libs
+    include_dirs = _include_dirs
+  }
+
+  group(target_name) {
+    public_configs = [ ":$config_name" ]
+  }
+}
+
+template("_fuchsia_fidl_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(target_cpu == "x64" || target_cpu == "arm64",
+         "We currently only support 'x64' and 'arm64' targets for fuchsia.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "fidl_library")
+
+  _deps = [ "../pkg:fidl_cpp" ]
+
+  library_name = string_replace(meta_json.name, "fuchsia.", "")
+  library_name_json = "$library_name.json"
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ ":$dep" ]
+  }
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = [ target_gen_dir ]
+  }
+
+  fidl_gen_target_name = "fidlgen_$target_name"
+  action(fidl_gen_target_name) {
+    script = "//build/fuchsia/fidl_gen_cpp"
+
+    library_name_slashes = string_replace(library_name, ".", "/")
+
+    inputs = [
+      invoker.meta,
+    ]
+
+    outputs = [
+      "$target_gen_dir/fuchsia/$library_name_slashes/c/fidl.h",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl.h",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl.cc",
+      "$target_gen_dir/fuchsia/$library_name_slashes/cpp/tables.cc",
+    ]
+
+    args = [
+      "--fidlc-bin",
+        rebase_path("$fuchsia_sdk_path/tools/fidlc"),
+      "--fidlgen-bin",
+        rebase_path("$fuchsia_sdk_path/tools/fidlgen"),
+      "--sdk-base",
+        rebase_path("$fuchsia_sdk_path"),
+      "--root",
+      rebase_path(invoker.meta),
+      "--json",
+      rebase_path("$target_gen_dir/$library_name_json"),
+      "--include-base",
+      rebase_path("$target_gen_dir"),
+      "--output-base-cc",
+      rebase_path("$target_gen_dir/fuchsia/$library_name_slashes/cpp/fidl"),
+      "--output-c-header",
+      rebase_path("$target_gen_dir/fuchsia/$library_name_slashes/c/fidl.h"),
+      "--output-c-tables",
+      rebase_path(
+          "$target_gen_dir/fuchsia/$library_name_slashes/cpp/tables.cc"),
+    ]
+  }
+
+  source_set(target_name) {
+    public_configs = [ ":$config_name" ]
+
+    sources = get_target_outputs(":$fidl_gen_target_name")
+
+    deps = [
+      ":$fidl_gen_target_name",
+    ]
+
+    public_deps = _deps
+  }
+}
+
+#
+# Produce a cc source library from invoker's json file.
+# Primary output is the source_set.
+#
+template("_fuchsia_cc_source_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  assert(meta_json.type == "cc_source_library")
+
+  _output_name = meta_json.name
+  _include_dirs = []
+  _public_headers = []
+  _sources = []
+  _deps = []
+
+  meta_json_include_dir = meta_json.include_dir
+  _include_dirs += [ rebase_path("$fuchsia_sdk_path/$meta_json_include_dir") ]
+
+  foreach(header, meta_json.headers) {
+    rebased_header = []
+    rebased_header = [ rebase_path("$fuchsia_sdk_path/$header") ]
+    _public_headers += rebased_header
+    _sources += rebased_header
+  }
+
+  foreach(source, meta_json.sources) {
+    _sources += [ "$fuchsia_sdk_path/$source" ]
+  }
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = _include_dirs
+  }
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ "../pkg:$dep" ]
+  }
+
+  foreach(dep, meta_json.fidl_deps) {
+    _deps += [ "../fidl:$dep" ]
+  }
+
+  source_set(target_name) {
+    output_name = _output_name
+    public = _public_headers
+    sources = _sources
+    public_configs = [ ":$config_name" ]
+    public_deps = _deps
+  }
+}
+
+template("_fuchsia_cc_prebuilt_library") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  meta_json = read_file(invoker.meta, "json")
+
+  _include_dirs = []
+  _deps = []
+  _libs = []
+
+  meta_json_include_dir = meta_json.include_dir
+  _include_dirs += [ "$fuchsia_sdk_path/$meta_json_include_dir" ]
+
+  foreach(dep, meta_json.deps) {
+    _deps += [ ":$dep" ]
+  }
+
+  meta_json_binaries = meta_json.binaries
+  if (target_cpu == "x64") {
+    meta_json_binaries_arch = meta_json_binaries.x64
+  } else {
+    meta_json_binaries_arch = meta_json_binaries.arm64
+  }
+  prebuilt_lib = meta_json_binaries_arch.link
+  _libs = [ "$fuchsia_sdk_path/$prebuilt_lib" ]
+
+  config_name = "config_$target_name"
+  config(config_name) {
+    include_dirs = _include_dirs
+    libs = _libs
+  }
+
+  group(target_name) {
+    public_configs = [ ":$config_name" ]
+    public_deps = _deps
+  }
+}
+
+#
+# Read SDK manifest json file and produce gn build targets for all
+# "enabled_parts" as specified by the template invoker.
+#
+# Fuchsia SDK manifest is primarily a "parts" array.
+#
+template("fuchsia_sdk") {
+  assert(defined(invoker.meta), "The meta.json file path must be specified.")
+  assert(defined(invoker.enabled_parts),
+         "A list containing the parts of the SDK to generate targets for.")
+
+  meta_json = read_file(invoker.meta, "json")
+
+  foreach(part, meta_json.parts) {
+    part_meta_json = {
+    }
+
+    part_meta = part.meta
+    part_meta_rebased = "$fuchsia_sdk_path/$part_meta"
+
+    part_meta_json = read_file(part_meta_rebased, "json")
+    subtarget_name = part_meta_json.name
+
+    foreach(enabled_part, invoker.enabled_parts) {
+      if (part.type == "cc_source_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_cc_source_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "sysroot") {
+        if (part.type == enabled_part) {
+          _fuchsia_sysroot(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "fidl_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_fidl_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      } else if (part.type == "cc_prebuilt_library") {
+        if (part.type == enabled_part) {
+          _fuchsia_cc_prebuilt_library(subtarget_name) {
+            meta = part_meta_rebased
+          }
+        }
+      }
+    }
+  }
+
+  group(target_name) {
+  }
+}
+
+#
+# Create package in 'gen' directory.
+#
+template("fuchsia_package") {
+  assert(defined(invoker.name), "The name of the package must be specified.")
+  assert(defined(invoker.version), "The package version must be specified.")
+
+  pkg_dir = target_gen_dir
+  pkg_name = invoker.name
+  pkg_version = invoker.version
+  pkg_manifest = invoker.pkg_manifest
+
+  pkg_id_path = "${pkg_dir}/meta/package"
+  gen_far_target_name = "gen_far_${target_name}"
+  pkg_archive = "${pkg_dir}/${pkg_name}-${pkg_version}.far"
+
+  action(gen_far_target_name) {
+    script = "//build/fuchsia/gen_package"
+
+    pm_binary = rebase_path("$fuchsia_sdk_path/tools/pm")
+
+    inputs = [
+      pm_binary,
+    ]
+
+    outputs = [
+      pkg_id_path,
+      pkg_archive,
+    ]
+
+    args = [
+      "--pm-bin",
+      pm_binary,
+      "--pkg-dir",
+      rebase_path(pkg_dir),
+      "--pkg-name",
+      pkg_name,
+      "--pkg-version",
+      "$pkg_version",
+      "--pkg-manifest",
+      rebase_path(pkg_manifest),
+    ]
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+  }
+
+  copy(target_name) {
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+
+    sources = [
+      pkg_archive,
+    ]
+
+    output_name = "${root_out_dir}/far/${pkg_name}.far"
+    outputs = [
+      output_name,
+    ]
+
+    deps = [
+      ":$gen_far_target_name",
+    ]
+  }
+}
+
+#
+# Places repo in output ('obj') directory.
+#
+template("fuchsia_repo") {
+  assert(defined(invoker.archives),
+         "The list of archives to publish must be specified.")
+  assert(defined(invoker.repo), "The location of the repo should be specified.")
+
+  action(target_name) {
+    if (defined(invoker.testonly)) {
+      testonly = invoker.testonly
+    }
+    script = "//build/fuchsia/gen_repo"
+
+    pm_binary = rebase_path("$fuchsia_sdk_path/tools/pm")
+    repo_directory = invoker.repo
+
+    inputs = [
+      pm_binary,
+    ]
+
+    archive_flags = []
+
+    foreach(archive, invoker.archives) {
+      assert(get_path_info(archive, "extension") == "far",
+             "Archive '$archive' does not have the .far extension.")
+      inputs += [ archive ]
+      archive_flags += [
+        "--archive",
+        rebase_path(archive),
+      ]
+    }
+
+    outputs = [
+      repo_directory,
+    ]
+
+    args = [
+             "--pm-bin",
+             pm_binary,
+             "--repo-dir",
+             rebase_path(repo_directory),
+           ] + archive_flags
+
+    if (defined(invoker.deps)) {
+      deps = invoker.deps
+    }
+  }
+}
diff --git a/build/fuchsia/skqp/BUILD.gn b/build/fuchsia/skqp/BUILD.gn
new file mode 100644
index 0000000..dbcdf16
--- /dev/null
+++ b/build/fuchsia/skqp/BUILD.gn
@@ -0,0 +1,134 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+
+import("//build/fuchsia/sdk.gni")
+
+pkg_dir = target_gen_dir
+pkg_name = "skqp_pkg"
+
+fuchsia_package(pkg_name) {
+  testonly = true
+  name = pkg_name
+  version = 0
+  deps = [
+    "//:skqp",
+    "//build/fuchsia",
+  ]
+  pkg_manifest = "${pkg_dir}/${target_name}.manifest"
+}
+
+fuchsia_repo("skqp_repo") {
+  testonly = true
+  deps = [
+    ":append_assets_to_manifest",
+    ":base_manifest",
+    ":skqp_pkg",
+  ]
+
+  # Put repo directory in out dir (not gen dir).
+  repo = "$target_out_dir/skqp_repo"
+  archives = [ "$root_out_dir/far/skqp_pkg.far" ]
+}
+
+#
+# Base manifest entries (w/o assets) for SKQP.
+#
+# TODO(rosasco): Convert this group() to generated_file() when a
+#                sufficiently modern version of gn rolls in.
+group("base_manifest") {
+  base_pkg_manifest = "${pkg_dir}/base_${pkg_name}.manifest"
+
+  skqp_exec = rebase_path("${root_out_dir}/skqp")
+  skqp_path = rebase_path("${target_gen_dir}")
+
+  sdk_dist_path = rebase_path("${fuchsia_sdk_path}/arch/${target_cpu}/dist")
+  sdk_sysroot_dist_path =
+      rebase_path("${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/dist/lib")
+
+  skqp_component_manifest = rebase_path("//build/fuchsia/skqp/skqp.cmx")
+
+  if (target_cpu == "x64" || target_cpu == "x86_64") {
+    clang_path =
+        rebase_path("${fuchsia_toolchain_path}/lib/x86_64-unknown-fuchsia/c++")
+  } else if (target_cpu == "arm64") {
+    clang_path =
+        rebase_path("${fuchsia_toolchain_path}/lib/aarch64-unknown-fuchsia/c++")
+  } else {
+    assert(false, "Unknown target cpu for Fuchsia target.")
+  }
+
+  manifest_entries = [
+    # Binary
+    "bin/skqp=${skqp_exec}",
+
+    # Meta Data
+    "meta/package=${skqp_path}/meta/package",
+    "meta/skqp.cmx=${skqp_component_manifest}",
+
+    # Shared Libs (clang c++)
+    "lib/libc++.so.2=${clang_path}/libc++.so.2",
+    "lib/libc++abi.so.1=${clang_path}/libc++abi.so.1",
+    "lib/libunwind.so.1=${clang_path}/libunwind.so.1",
+
+    # Shared Libs (sdk dist)
+    "lib/libvulkan.so=${sdk_dist_path}/libvulkan.so",
+    "lib/libasync-default.so=${sdk_dist_path}/libasync-default.so",
+    "lib/libfdio.so=${sdk_dist_path}/libfdio.so",
+    "lib/libtrace-engine.so=${sdk_dist_path}/libtrace-engine.so",
+
+    # Loader
+    "lib/ld.so.1=${sdk_sysroot_dist_path}/ld.so.1",
+
+    # Vulkan ICD / Validation / Loader
+    "lib/libvulkan.so=${sdk_dist_path}/libvulkan.so",
+  ]
+
+  # TODO(rosasco): Add 2 lines when generated_file() switch is implemented
+  #                and remove the 'write_file' below.
+  # contents = manifest_entries
+  # outputs = [ base_pkg_manifest ]
+
+  write_file(base_pkg_manifest, manifest_entries)
+}
+
+#
+# Asset manifest entries for SKQP.
+#
+action("append_assets_to_manifest") {
+  script = "append_assets_to_manifest"
+
+  base_pkg_manifest = "${pkg_dir}/base_${pkg_name}.manifest"
+  pkg_manifest = "${pkg_dir}/${pkg_name}.manifest"
+  assets_path = "//platform_tools/android/apps/skqp/src/main/assets"
+
+  inputs = [
+    assets_path,
+  ]
+
+  outputs = [
+    pkg_manifest,
+  ]
+
+  manifest_deps = "$target_out_dir/skqp_manifest.d"
+
+  args = [
+    "--root_dir",
+    rebase_path(assets_path),
+    "--base_manifest",
+    rebase_path(base_pkg_manifest),
+    "--manifest",
+    rebase_path(pkg_manifest),
+    "--deps",
+    rebase_path(manifest_deps),
+    "--root_build_dir",
+    rebase_path(root_out_dir),
+  ]
+
+  deps = [
+    ":base_manifest",
+  ]
+  depfile = manifest_deps
+}
diff --git a/build/fuchsia/skqp/README.md b/build/fuchsia/skqp/README.md
new file mode 100644
index 0000000..be357cc
--- /dev/null
+++ b/build/fuchsia/skqp/README.md
@@ -0,0 +1,18 @@
+# CIPD Package Creation and Upload Procedure
+These steps assume the creation of the arm64 CIPD package as an example.  Because the package requires a path from the output directory of the build, the `gn gen` arguments must match the prescribed path declared in `cipd_arm64.yaml` in order for this CIPD package creation and upload to succeed.
+
+## Create CIPD Package
+```
+cipd create -pkg-def=cipd_arm64.yaml
+```
+
+## Set CIPD Ref of `latest`
+If applicable, set `latest` ref to new CIPD package.
+```
+cipd set-ref skia/fuchsia/skqp/arch/arm64 -ref latest -version mdhS7sryb2zxQuXT803Dv_XZ0r7B5j8jSbZmIi0JvOcC
+```
+
+## Set the git-commit Tag
+```
+cipd set-tag skia/fuchsia/skqp/arch/arm64 -tag=git-commit:9c2b7cfe9080c6c4692234667a671db216a2e229 -version mdhS7sryb2zxQuXT803Dv_XZ0r7B5j8jSbZmIi0JvOcC
+```
diff --git a/build/fuchsia/skqp/append_assets_to_manifest b/build/fuchsia/skqp/append_assets_to_manifest
new file mode 100755
index 0000000..f1a0d89
--- /dev/null
+++ b/build/fuchsia/skqp/append_assets_to_manifest
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+ Opens |base_manifest| and copies the contents to |manifest| then traverses
+ |root_dir| and appends every file as a Fuchsia package manifest entry to
+ |manifest|.
+"""
+
+import argparse
+import os
+import subprocess
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--root_dir', dest='root_dir', action='store', required=True)
+parser.add_argument('--base_manifest', dest='base_manifest', action='store', required=True)
+parser.add_argument('--manifest', dest='manifest', action='store', required=True)
+parser.add_argument('--deps', dest='deps', action='store', required=True)
+parser.add_argument('--root_build_dir', dest='root_build_dir', action='store', required=True)
+args = parser.parse_args()
+
+root_dir = args.root_dir
+if not os.path.exists(root_dir):
+    print "--root_dir path specified: " + root_dir + " doesn't exist.\n"
+    exit(1)
+
+base_manifest = args.base_manifest
+if not os.path.exists(base_manifest):
+    print "--base_manifest specified: " + base_manifest + " doesn't exist.\n"
+    exit(1)
+
+manifest = args.manifest
+root_build_dir = args.root_build_dir
+
+# Prepend |base_manifest| contents to |manifest|.
+deps_file = open(args.deps, 'w')
+relative_path = os.path.relpath(args.manifest, root_build_dir)
+deps_file.write('%s: ' % relative_path)
+
+out_file = open(manifest, 'w')
+with open(base_manifest, 'r') as in_file:
+    base_content = in_file.readlines()
+
+for base_line in base_content:
+    out_file.write(base_line)
+    base_line_list = base_line.split("=")
+    if len(base_line_list) != 2:
+        print "Error: Base manifest line format error. Expected \"lhs=rhs\" but got: " + base_line
+        exit(1)
+    base_line_rhs = base_line_list[1].strip()
+    relative_path = os.path.relpath(base_line_rhs, root_build_dir)
+    deps_file.write(relative_path + " ")
+
+# Append all files discovered under |root_dir| to |manifest|.
+files = subprocess.check_output(["find", root_dir, "-type", "f"])
+file_lines = files.splitlines()
+
+for file in file_lines:
+        source = file
+        if not source.startswith(root_dir):
+            print "Error: source path " + source + " is not under |root_dir|\n"
+            exit(1)
+        dest = source[len(root_dir):]
+        out_file.write('data%s=' % dest)
+        out_file.write('%s\n' % source)
+        relative_path = os.path.relpath(source, root_build_dir)
+        deps_file.write(relative_path + " ")
+
+out_file.close()
+deps_file.close()
diff --git a/build/fuchsia/skqp/cipd_arm64.yaml b/build/fuchsia/skqp/cipd_arm64.yaml
new file mode 100644
index 0000000..e6a459d
--- /dev/null
+++ b/build/fuchsia/skqp/cipd_arm64.yaml
@@ -0,0 +1,7 @@
+package: skia/fuchsia/skqp/arch/arm64
+description: ARM64 build of Skia's skqp test suite for Fuchsia
+install_mode: copy
+root: ../../..
+data:
+  - file: build/fuchsia/skqp/test_manifest.json
+  - file: out/fuchsia-arm64/far/skqp_pkg.far
diff --git a/build/fuchsia/skqp/skqp.cmx b/build/fuchsia/skqp/skqp.cmx
new file mode 100644
index 0000000..00e2082
--- /dev/null
+++ b/build/fuchsia/skqp/skqp.cmx
@@ -0,0 +1,20 @@
+{
+    "program": {
+        "binary": "bin/skqp",
+        "args" : [ "/pkg/data", "tmp/logs" ]
+    },
+    "sandbox": {
+        "dev": [
+            "class/display-controller"
+        ],
+        "features": [
+            "vulkan",
+            "system-temp"
+        ],
+        "services": [
+            "fuchsia.sysmem.Allocator",
+            "fuchsia.tracing.provider.Registry",
+            "fuchsia.vulkan.loader.Loader"
+        ]
+    }
+}
diff --git a/build/fuchsia/skqp/test_manifest.json b/build/fuchsia/skqp/test_manifest.json
new file mode 100644
index 0000000..4e67279
--- /dev/null
+++ b/build/fuchsia/skqp/test_manifest.json
@@ -0,0 +1,6 @@
+[
+  {
+    "package": "skqp_pkg",
+    "component_name": "skqp"
+  }
+]
diff --git a/build/fuchsia/sysroot/BUILD.gn b/build/fuchsia/sysroot/BUILD.gn
new file mode 100644
index 0000000..69d93cd
--- /dev/null
+++ b/build/fuchsia/sysroot/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 Google LLC. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_fuchsia)
+import("//build/fuchsia/sdk.gni")
+
+fuchsia_sdk("sdk_sysroot") {
+  meta = fuchsia_sdk_manifest_path
+  enabled_parts = [ "sysroot" ]
+}
diff --git a/build/fuchsia/update_fuchsia_sdk b/build/fuchsia/update_fuchsia_sdk
new file mode 100755
index 0000000..065c4b6
--- /dev/null
+++ b/build/fuchsia/update_fuchsia_sdk
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+update_fuchsia_sdk
+
+  Downloads both the Fuchsia SDK and Fuchsia-compatible clang
+  zip archives from chrome infra (CIPD) and extracts them to
+  the arg-provide |sdk_dir| and |clang_dir| respectively.  This
+  provides the complete toolchain required to build Fuchsia binaries
+  from the Fuchsia SDK.
+
+"""
+
+import argparse
+import errno
+import logging
+import os
+import platform
+import shutil
+import subprocess
+import tempfile
+
+def MessageExit(message):
+  logging.error(message)
+  sys.exit(1)
+
+# Verify that "cipd" tool is readily available.
+def CipdLives():
+    err_msg = "Cipd not found, please install. See: " + \
+              "https://commondatastorage.googleapis.com/chrome-infra-docs/flat" + \
+              "/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up"
+    try:
+        subprocess.call(["cipd", "--version"])
+    except OSError as e:
+        if e.errno == errno.ENOENT:
+            MessageExit(err_msg)
+        else:
+            MessageExit("cipd command execution failed.")
+
+# Download and unzip CIPD package archive.
+def DownloadAndUnzip(pkg_name, version, cipd_cache_dir, output_dir):
+  pkg_suffix = pkg_name.replace('/', '-') + ".zip"
+  zip_file = tempfile.NamedTemporaryFile(suffix=pkg_suffix, delete=False)
+  cipd_cmd = "cipd pkg-fetch " + pkg_name + " -version \"" + version + "\" -out " + \
+      zip_file.name + " -cache-dir " + cipd_cache_dir
+  unzip_cmd = "unzip -q " + zip_file.name + " -d " + output_dir
+  os.system(cipd_cmd)
+  os.system(unzip_cmd)
+
+def Main():
+  CipdLives()
+  parser = argparse.ArgumentParser()
+  parser.add_argument("-sdk_dir", type=str,
+          help="Destination directory for the fuchsia SDK.")
+  parser.add_argument("-clang_dir", type=str,
+          help="Destination directory for the fuchsia toolchain.")
+  parser.add_argument("-overwrite_dirs", type=bool, default=False,
+          help="REMOVES existing sdk and clang dirs and makes new ones.  When false " +
+               "  the unzip command issue will require file overwrite confirmation.")
+  parser.add_argument("-cipd_cache_dir", type=str, default="/tmp", required=False,
+          help="Cache directory for CIPD downloads to prevent redundant downloads.")
+  parser.add_argument("-cipd_sdk_version", type=str, default="latest", required=False,
+          help="CIPD sdk version to download, e.g.: git_revision:fce11c6904c888e6d39f71e03806a540852dec41")
+  parser.add_argument("-cipd_clang_version", type=str, default="latest", required=False,
+          help="CIPD clang version to download, e.g.: git_revision:fce11c6904c888e6d39f71e03806a540852dec41")
+  args = parser.parse_args()
+
+  sdk_dir = args.sdk_dir
+  clang_dir = args.clang_dir
+  cipd_sdk_version = args.cipd_sdk_version
+  cipd_clang_version = args.cipd_clang_version
+
+  if args.overwrite_dirs:
+    dirs = [sdk_dir, clang_dir]
+    for curr_dir in dirs:
+      try:
+        if os.path.exists(curr_dir):
+            shutil.rmtree(curr_dir)
+        os.makedirs(curr_dir)
+      except OSError:
+        MessageExit("Creation of the directory %s failed" % curr_dir)
+  else:
+    # Make dirs for sdk and clang.
+    if not os.path.exists(sdk_dir):
+        os.makedirs(sdk_dir)
+    if not os.path.exists(clang_dir):
+        os.makedirs(clang_dir)
+
+    # Verify that existing dirs are writable.
+    if (not os.access(sdk_dir, os.W_OK)) or (not os.path.isdir(sdk_dir)):
+      MessageExit("Can't write to sdk dir " + sdk_dir)
+    if (not os.access(clang_dir, os.W_OK)) or (not os.path.isdir(clang_dir)):
+      MessageExit("Can't write to clang dir " + clang_dir)
+ 
+  ostype = platform.system()
+  if ostype == "Linux":
+    os_string = "linux-amd64"
+  elif ostype == "Darwin":
+    os_string = "mac-amd64"
+  else:
+    MessageExit("Unknown host " + ostype)
+
+  # |sdk_pkg| and |clang_pkg| below are prescribed paths defined by chrome-infra.
+  sdk_pkg = "fuchsia/sdk/core/" + os_string
+  DownloadAndUnzip(sdk_pkg, cipd_sdk_version, args.cipd_cache_dir, sdk_dir)
+  clang_pkg = "fuchsia/clang/" + os_string
+  DownloadAndUnzip(clang_pkg, cipd_clang_version, args.cipd_cache_dir, clang_dir)
+
+if __name__ == "__main__":
+  import sys
+  Main()
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 5c0fcac..6eb307d 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -998,9 +998,6 @@
 #endif
     VIA("serialize", ViaSerialization,     wrapped);
     VIA("pic",       ViaPicture,           wrapped);
-    VIA("tiles",     ViaTiles, 256, 256, nullptr,            wrapped);
-    VIA("tiles_rt",  ViaTiles, 256, 256, new SkRTreeFactory, wrapped);
-
     VIA("ddl",       ViaDDL, 1, 3,         wrapped);
     VIA("ddl2",      ViaDDL, 2, 3,         wrapped);
 
@@ -1237,7 +1234,7 @@
         };
 
         skcms_TransferFunction tf;
-        cs->transferFn(&tf.g);
+        cs->transferFn(&tf);
         switch (classify_transfer_fn(tf)) {
             case sRGBish_TF:
                 if (tf.a == 1 && tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0) {
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index b72c830..e573475 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -15,7 +15,6 @@
 #include "include/core/SkExecutor.h"
 #include "include/core/SkImageGenerator.h"
 #include "include/core/SkMallocPixelRef.h"
-#include "include/core/SkMultiPictureDraw.h"
 #include "include/core/SkPictureRecorder.h"
 #include "include/core/SkStream.h"
 #include "include/core/SkSurface.h"
@@ -60,7 +59,7 @@
 
 #if defined(SK_ENABLE_SKOTTIE)
     #include "modules/skottie/include/Skottie.h"
-    #include "modules/skottie/utils/SkottieUtils.h"
+    #include "modules/skresources/include/SkResources.h"
 #endif
 
 #if defined(SK_XML)
@@ -1117,8 +1116,8 @@
 Error SkottieSrc::draw(SkCanvas* canvas) const {
     auto animation = skottie::Animation::Builder()
         .setResourceProvider(
-                skottie_utils::DataURIResourceProviderProxy::Make(
-                    skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str()),
+                skresources::DataURIResourceProviderProxy::Make(
+                    skresources::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str()),
                                                               /*predecode=*/true),
                     /*predecode=*/true))
         .makeFromFile(fPath.c_str());
@@ -1865,59 +1864,6 @@
 
 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
-ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink)
-    : Via(sink)
-    , fW(w)
-    , fH(h)
-    , fFactory(factory) {}
-
-Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
-    auto size = src.size();
-    SkPictureRecorder recorder;
-    Error err = src.draw(recorder.beginRecording(SkIntToScalar(size.width()),
-                                                 SkIntToScalar(size.height()),
-                                                 fFactory.get()));
-    if (!err.isEmpty()) {
-        return err;
-    }
-    sk_sp<SkPicture> pic(recorder.finishRecordingAsPicture());
-
-    return draw_to_canvas(fSink.get(), bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
-        const int xTiles = (size.width()  + fW - 1) / fW,
-                  yTiles = (size.height() + fH - 1) / fH;
-        SkMultiPictureDraw mpd(xTiles*yTiles);
-        SkTArray<sk_sp<SkSurface>> surfaces;
-//        surfaces.setReserve(xTiles*yTiles);
-
-        SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
-        for (int j = 0; j < yTiles; j++) {
-            for (int i = 0; i < xTiles; i++) {
-                // This lets our ultimate Sink determine the best kind of surface.
-                // E.g., if it's a GpuSink, the surfaces and images are textures.
-                auto s = canvas->makeSurface(info);
-                if (!s) {
-                    s = SkSurface::MakeRaster(info);  // Some canvases can't create surfaces.
-                }
-                surfaces.push_back(s);
-                SkCanvas* c = s->getCanvas();
-                c->translate(SkIntToScalar(-i * fW),
-                             SkIntToScalar(-j * fH));  // Line up the canvas with this tile.
-                mpd.add(c, pic.get());
-            }
-        }
-        mpd.draw();
-        for (int j = 0; j < yTiles; j++) {
-            for (int i = 0; i < xTiles; i++) {
-                sk_sp<SkImage> image(surfaces[i+xTiles*j]->makeImageSnapshot());
-                canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
-            }
-        }
-        return "";
-    });
-}
-
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
 ViaDDL::ViaDDL(int numReplays, int numDivisions, Sink* sink)
         : Via(sink), fNumReplays(numReplays), fNumDivisions(numDivisions) {}
 
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index 5b7338b..63b3f7f 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -525,15 +525,6 @@
     Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
 };
 
-class ViaTiles : public Via {
-public:
-    ViaTiles(int w, int h, SkBBHFactory*, Sink*);
-    Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
-private:
-    const int                   fW, fH;
-    std::unique_ptr<SkBBHFactory> fFactory;
-};
-
 class ViaDDL : public Via {
 public:
     ViaDDL(int numReplays, int numDivisions, Sink* sink);
diff --git a/docs/examples/Arc.cpp b/docs/examples/Arc.cpp
index 7b6daaf..bb96907 100644
--- a/docs/examples/Arc.cpp
+++ b/docs/examples/Arc.cpp
@@ -34,7 +34,7 @@
                 path.arcTo({56, 56}, {32, 56}, 24);
                 break;
             case '5':
-                path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPath::kCW_Direction, {32, 56});
+                path.arcTo({24, 24}, 0, SkPath::kSmall_ArcSize, SkPathDirection::kCW, {32, 56});
                 break;
             case '6':
                 path.conicTo({56, 56}, {32, 56}, SK_ScalarRoot2Over2);
diff --git a/docs/examples/Canvas_clipPath.cpp b/docs/examples/Canvas_clipPath.cpp
index 3b6d43d8..c87ccc1 100644
--- a/docs/examples/Canvas_clipPath.cpp
+++ b/docs/examples/Canvas_clipPath.cpp
@@ -8,13 +8,13 @@
     paint.setAntiAlias(true);
     SkPath path;
     path.addRect({20, 30, 100, 110});
-    path.setFillType(SkPath::kInverseWinding_FillType);
+    path.setFillType(SkPathFillType::kInverseWinding);
     canvas->save();
     canvas->clipPath(path, SkClipOp::kDifference, false);
     canvas->drawCircle(70, 100, 60, paint);
     canvas->restore();
     canvas->translate(100, 100);
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     canvas->clipPath(path, SkClipOp::kIntersect, false);
     canvas->drawCircle(70, 100, 60, paint);
 }
diff --git a/docs/examples/Canvas_clipPath_2.cpp b/docs/examples/Canvas_clipPath_2.cpp
index 963c8d7..1bea1e0 100644
--- a/docs/examples/Canvas_clipPath_2.cpp
+++ b/docs/examples/Canvas_clipPath_2.cpp
@@ -9,13 +9,13 @@
     SkPath path;
     path.addRect({20, 15, 100, 95});
     path.addRect({50, 65, 130, 135});
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     canvas->save();
     canvas->clipPath(path, SkClipOp::kIntersect);
     canvas->drawCircle(70, 85, 60, paint);
     canvas->restore();
     canvas->translate(100, 100);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->clipPath(path, SkClipOp::kIntersect);
     canvas->drawCircle(70, 85, 60, paint);
 }
diff --git a/docs/examples/Canvas_clipPath_3.cpp b/docs/examples/Canvas_clipPath_3.cpp
index 9988a80..3a1c516 100644
--- a/docs/examples/Canvas_clipPath_3.cpp
+++ b/docs/examples/Canvas_clipPath_3.cpp
@@ -10,13 +10,13 @@
     SkPoint poly[] = {{20, 20}, { 80, 20}, { 80,  80}, {40,  80},
                       {40, 40}, {100, 40}, {100, 100}, {20, 100}};
     path.addPoly(poly, SK_ARRAY_COUNT(poly), true);
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     canvas->save();
     canvas->clipPath(path, SkClipOp::kIntersect);
     canvas->drawCircle(50, 50, 45, paint);
     canvas->restore();
     canvas->translate(100, 100);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->clipPath(path, SkClipOp::kIntersect);
     canvas->drawCircle(50, 50, 45, paint);
 }
diff --git a/docs/examples/Canvas_drawPath.cpp b/docs/examples/Canvas_drawPath.cpp
index d62374d..2a5460b 100644
--- a/docs/examples/Canvas_drawPath.cpp
+++ b/docs/examples/Canvas_drawPath.cpp
@@ -24,9 +24,9 @@
         canvas->translate(-240, 60);
     }
     paint.setStyle(SkPaint::kFill_Style);
-    for (auto fill : { SkPath::kWinding_FillType,
-                       SkPath::kEvenOdd_FillType,
-                       SkPath::kInverseWinding_FillType } ) {
+    for (auto fill : { SkPathFillType::kWinding,
+                       SkPathFillType::kEvenOdd,
+                       SkPathFillType::kInverseWinding } ) {
         path.setFillType(fill);
         canvas->save();
         canvas->clipRect({0, 10, 80, 70});
diff --git a/docs/examples/Path_ArcSize.cpp b/docs/examples/Path_ArcSize.cpp
index b99a237..1c7b683 100644
--- a/docs/examples/Path_ArcSize.cpp
+++ b/docs/examples/Path_ArcSize.cpp
@@ -7,12 +7,12 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     paint.setStyle(SkPaint::kStroke_Style);
-    for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto sweep: { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
             SkPath path;
             path.moveTo({120, 50});
             path.arcTo(70, 40, 30, arcSize, sweep, 156, 100);
-            if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
+            if (SkPathDirection::kCCW == sweep && SkPath::kLarge_ArcSize == arcSize) {
                 paint.setColor(SK_ColorBLUE);
                 paint.setStrokeWidth(3);
             }
diff --git a/docs/examples/Path_ConvertToNonInverseFillType.cpp b/docs/examples/Path_ConvertToNonInverseFillType.cpp
index 12e2d1f..d60857f 100644
--- a/docs/examples/Path_ConvertToNonInverseFillType.cpp
+++ b/docs/examples/Path_ConvertToNonInverseFillType.cpp
@@ -3,25 +3,25 @@
 #include "tools/fiddle/examples.h"
 // HASH=319f6b124458dcc0f9ce4d7bbde65810
 REG_FIDDLE(Path_ConvertToNonInverseFillType, 256, 256, true, 0) {
-#define nameValue(fill) { SkPath::fill, #fill }
+#define nameValue(fill) { SkPathFillType::fill, #fill }
 
 void draw(SkCanvas* canvas) {
     struct {
-        SkPath::FillType fill;
+        SkPathFillType fill;
         const char* name;
     } fills[] = {
-        nameValue(kWinding_FillType),
-        nameValue(kEvenOdd_FillType),
-        nameValue(kInverseWinding_FillType),
-        nameValue(kInverseEvenOdd_FillType),
+        nameValue(kWinding),
+        nameValue(kEvenOdd),
+        nameValue(kInverseWinding),
+        nameValue(kInverseEvenOdd),
     };
     for (unsigned i = 0; i < SK_ARRAY_COUNT(fills); ++i) {
-        if (fills[i].fill != (SkPath::FillType) i) {
+        if (fills[i].fill != (SkPathFillType) i) {
             SkDebugf("fills array order does not match FillType enum order");
             break;
         }
         SkDebugf("ConvertToNonInverseFillType(%s) == %s\n", fills[i].name,
-                fills[(int) SkPath::ConvertToNonInverseFillType(fills[i].fill)].name);
+                fills[(int) SkPathFillType_ConvertToNonInverse(fills[i].fill)].name);
     }
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_Direction.cpp b/docs/examples/Path_Direction.cpp
index dab6ba9..bbc74a2 100644
--- a/docs/examples/Path_Direction.cpp
+++ b/docs/examples/Path_Direction.cpp
@@ -16,14 +16,14 @@
     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 320, 0,
                              SkPath1DPathEffect::kRotate_Style));
-    for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto direction : { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         canvas->drawRect(rect, rectPaint);
         for (unsigned start : { 0, 1, 2, 3 } ) {
            SkPath path;
            path.addRect(rect, direction, start);
            canvas->drawPath(path, arrowPaint);
        }
-       canvas->drawString(SkPath::kCW_Direction == direction ? "CW" : "CCW",  rect.centerX(),
+       canvas->drawString(SkPathDirection::kCW == direction ? "CW" : "CCW",  rect.centerX(),
             rect.centerY(), textPaint);
        canvas->translate(120, 0);
     }
diff --git a/docs/examples/Path_FillType_a.cpp b/docs/examples/Path_FillType_a.cpp
index 5931cab..d19c2c3 100644
--- a/docs/examples/Path_FillType_a.cpp
+++ b/docs/examples/Path_FillType_a.cpp
@@ -5,17 +5,17 @@
 REG_FIDDLE(Path_FillType_a, 256, 100, false, 0) {
 void draw(SkCanvas* canvas) {
    SkPath path;
-   path.addRect({10, 10, 30, 30}, SkPath::kCW_Direction);
-   path.addRect({20, 20, 40, 40}, SkPath::kCW_Direction);
-   path.addRect({10, 60, 30, 80}, SkPath::kCW_Direction);
-   path.addRect({20, 70, 40, 90}, SkPath::kCCW_Direction);
+   path.addRect({10, 10, 30, 30}, SkPathDirection::kCW);
+   path.addRect({20, 20, 40, 40}, SkPathDirection::kCW);
+   path.addRect({10, 60, 30, 80}, SkPathDirection::kCW);
+   path.addRect({20, 70, 40, 90}, SkPathDirection::kCCW);
    SkPaint strokePaint;
    strokePaint.setStyle(SkPaint::kStroke_Style);
    SkRect clipRect = {0, 0, 51, 100};
    canvas->drawPath(path, strokePaint);
    SkPaint fillPaint;
-   for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
-                      SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
+   for (auto fillType : { SkPathFillType::kWinding, SkPathFillType::kEvenOdd,
+                      SkPathFillType::kInverseWinding, SkPathFillType::kInverseEvenOdd } ) {
         canvas->translate(51, 0);
         canvas->save();
         canvas->clipRect(clipRect);
diff --git a/docs/examples/Path_FillType_b.cpp b/docs/examples/Path_FillType_b.cpp
index 6064fe0..4c1b83c 100644
--- a/docs/examples/Path_FillType_b.cpp
+++ b/docs/examples/Path_FillType_b.cpp
@@ -6,8 +6,8 @@
 REG_FIDDLE(Path_FillType_b, 256, 230, false, 0) {
 void draw(SkCanvas* canvas) {
    SkPath path;
-   path.addRect({20, 10, 80, 70}, SkPath::kCW_Direction);
-   path.addRect({40, 30, 100, 90}, SkPath::kCW_Direction);
+   path.addRect({20, 10, 80, 70}, SkPathDirection::kCW);
+   path.addRect({40, 30, 100, 90}, SkPathDirection::kCW);
    SkPaint strokePaint;
    strokePaint.setStyle(SkPaint::kStroke_Style);
    SkRect clipRect = {0, 0, 128, 128};
@@ -22,8 +22,8 @@
    canvas->scale(.5f, .5f);
    canvas->drawString("inverse", 384, 150, textPaint);
    SkPaint fillPaint;
-   for (auto fillType : { SkPath::kWinding_FillType, SkPath::kEvenOdd_FillType,
-                      SkPath::kInverseWinding_FillType, SkPath::kInverseEvenOdd_FillType } ) {
+   for (auto fillType : { SkPathFillType::kWinding, SkPathFillType::kEvenOdd,
+                      SkPathFillType::kInverseWinding, SkPathFillType::kInverseEvenOdd } ) {
         canvas->save();
         canvas->clipRect(clipRect);
         path.setFillType(fillType);
diff --git a/docs/examples/Path_IsInverseFillType.cpp b/docs/examples/Path_IsInverseFillType.cpp
index 1b2cb10..3d2e5f0 100644
--- a/docs/examples/Path_IsInverseFillType.cpp
+++ b/docs/examples/Path_IsInverseFillType.cpp
@@ -3,20 +3,20 @@
 #include "tools/fiddle/examples.h"
 // HASH=1453856a9d0c73e8192bf298c4143563
 REG_FIDDLE(Path_IsInverseFillType, 256, 256, true, 0) {
-#define nameValue(fill) { SkPath::fill, #fill }
+#define nameValue(fill) { SkPathFillType::fill, #fill }
 
 void draw(SkCanvas* canvas) {
     struct {
-        SkPath::FillType fill;
+        SkPathFillType fill;
         const char* name;
     } fills[] = {
-        nameValue(kWinding_FillType),
-        nameValue(kEvenOdd_FillType),
-        nameValue(kInverseWinding_FillType),
-        nameValue(kInverseEvenOdd_FillType),
+        nameValue(kWinding),
+        nameValue(kEvenOdd),
+        nameValue(kInverseWinding),
+        nameValue(kInverseEvenOdd),
     };
     for (auto fill: fills ) {
-        SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPath::IsInverseFillType(fill.fill) ?
+        SkDebugf("IsInverseFillType(%s) == %s\n", fill.name, SkPathFillType_IsInverse(fill.fill) ?
                  "true" : "false");
     }
 }
diff --git a/docs/examples/Path_addCircle.cpp b/docs/examples/Path_addCircle.cpp
index dbea5b7..73cca56 100644
--- a/docs/examples/Path_addCircle.cpp
+++ b/docs/examples/Path_addCircle.cpp
@@ -10,7 +10,7 @@
     paint.setStrokeWidth(10);
     for (int size = 10; size < 300; size += 20) {
         SkPath path;
-        path.addCircle(128, 128, size, SkPath::kCW_Direction);
+        path.addCircle(128, 128, size, SkPathDirection::kCW);
         canvas->drawPath(path, paint);
     }
 }
diff --git a/docs/examples/Path_addOval_2.cpp b/docs/examples/Path_addOval_2.cpp
index ba07e54..1af9977 100644
--- a/docs/examples/Path_addOval_2.cpp
+++ b/docs/examples/Path_addOval_2.cpp
@@ -16,7 +16,7 @@
     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
                              SkPath1DPathEffect::kRotate_Style));
-    for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto direction : { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         for (unsigned start : { 0, 1, 2, 3 } ) {
            SkPath path;
            path.addOval(rect, direction, start);
@@ -26,7 +26,7 @@
            canvas->translate(64, 0);
        }
        canvas->translate(-256, 72);
-       canvas->drawString(SkPath::kCW_Direction == direction ? "clockwise" : "counterclockwise",
+       canvas->drawString(SkPathDirection::kCW == direction ? "clockwise" : "counterclockwise",
                           128, 0, textPaint);
     }
 }
diff --git a/docs/examples/Path_addPath_2.cpp b/docs/examples/Path_addPath_2.cpp
index e00ff7c..8d72404 100644
--- a/docs/examples/Path_addPath_2.cpp
+++ b/docs/examples/Path_addPath_2.cpp
@@ -7,7 +7,7 @@
     SkPaint paint;
     paint.setStyle(SkPaint::kStroke_Style);
     SkPath dest, path;
-    path.addOval({-80, 20, 0, 60}, SkPath::kCW_Direction, 1);
+    path.addOval({-80, 20, 0, 60}, SkPathDirection::kCW, 1);
     for (int i = 0; i < 2; i++) {
         dest.addPath(path, SkPath::kExtend_AddPathMode);
         dest.offset(100, 0);
diff --git a/docs/examples/Path_addPath_3.cpp b/docs/examples/Path_addPath_3.cpp
index 2fb4761..b865d2b 100644
--- a/docs/examples/Path_addPath_3.cpp
+++ b/docs/examples/Path_addPath_3.cpp
@@ -7,7 +7,7 @@
     SkPaint paint;
     paint.setStyle(SkPaint::kStroke_Style);
     SkPath dest, path;
-    path.addOval({20, 20, 200, 120}, SkPath::kCW_Direction, 1);
+    path.addOval({20, 20, 200, 120}, SkPathDirection::kCW, 1);
     for (int i = 0; i < 6; i++) {
         SkMatrix matrix;
         matrix.reset();
diff --git a/docs/examples/Path_addRRect_2.cpp b/docs/examples/Path_addRRect_2.cpp
index 2f14659..43f7c87 100644
--- a/docs/examples/Path_addRRect_2.cpp
+++ b/docs/examples/Path_addRRect_2.cpp
@@ -13,7 +13,7 @@
     canvas->drawPath(path, paint);
     for (int start = 0; start < 8; ++start) {
         SkPath textPath;
-        textPath.addRRect(rrect, SkPath::kCW_Direction, start);
+        textPath.addRRect(rrect, SkPathDirection::kCW, start);
         SkPathMeasure pathMeasure(textPath, false);
         SkPoint position;
         SkVector tangent;
diff --git a/docs/examples/Path_addRect.cpp b/docs/examples/Path_addRect.cpp
index e51b8ef..694e420 100644
--- a/docs/examples/Path_addRect.cpp
+++ b/docs/examples/Path_addRect.cpp
@@ -11,10 +11,10 @@
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
     SkPath path;
-    path.addRect({20, 20, 100, 100}, SkPath::kCW_Direction);
+    path.addRect({20, 20, 100, 100}, SkPathDirection::kCW);
     canvas->drawPath(path, paint);
     path.rewind();
-    path.addRect({140, 20, 220, 100}, SkPath::kCCW_Direction);
+    path.addRect({140, 20, 220, 100}, SkPathDirection::kCCW);
     canvas->drawPath(path, paint);
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_addRect_2.cpp b/docs/examples/Path_addRect_2.cpp
index 39d1044..798eaae 100644
--- a/docs/examples/Path_addRect_2.cpp
+++ b/docs/examples/Path_addRect_2.cpp
@@ -14,7 +14,7 @@
     arrowPath.addPoly(arrow, SK_ARRAY_COUNT(arrow), true);
     arrowPaint.setPathEffect(SkPath1DPathEffect::Make(arrowPath, 176, 0,
                              SkPath1DPathEffect::kRotate_Style));
-    for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto direction : { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         for (unsigned start : { 0, 1, 2, 3 } ) {
            SkPath path;
            path.addRect(rect, direction, start);
diff --git a/docs/examples/Path_addRect_3.cpp b/docs/examples/Path_addRect_3.cpp
index 1022328..b5bb877 100644
--- a/docs/examples/Path_addRect_3.cpp
+++ b/docs/examples/Path_addRect_3.cpp
@@ -10,7 +10,7 @@
     float intervals[] = { 5, 21.75f };
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
-    for (auto direction : { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto direction : { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         SkPath path;
         path.addRect(20, 20, 100, 100, direction);
         canvas->drawPath(path, paint);
diff --git a/docs/examples/Path_arcTo_4.cpp b/docs/examples/Path_arcTo_4.cpp
index 8de4927..37395d7 100644
--- a/docs/examples/Path_arcTo_4.cpp
+++ b/docs/examples/Path_arcTo_4.cpp
@@ -7,12 +7,12 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     paint.setStyle(SkPaint::kStroke_Style);
-    for (auto sweep: { SkPath::kCW_Direction, SkPath::kCCW_Direction } ) {
+    for (auto sweep: { SkPathDirection::kCW, SkPathDirection::kCCW } ) {
         for (auto arcSize : { SkPath::kSmall_ArcSize, SkPath::kLarge_ArcSize } ) {
             SkPath path;
             path.moveTo({120, 50});
             path.arcTo(70, 40, 30, arcSize, sweep, 120.1f, 50);
-            if (SkPath::kCCW_Direction == sweep && SkPath::kLarge_ArcSize == arcSize) {
+            if (SkPathDirection::kCCW == sweep && SkPath::kLarge_ArcSize == arcSize) {
                 paint.setColor(SK_ColorBLUE);
                 paint.setStrokeWidth(3);
             }
diff --git a/docs/examples/Path_dumpHex.cpp b/docs/examples/Path_dumpHex.cpp
index 2578ab5..25af4cd 100644
--- a/docs/examples/Path_dumpHex.cpp
+++ b/docs/examples/Path_dumpHex.cpp
@@ -7,7 +7,7 @@
     SkPath path, copy;
     path.lineTo(6.f / 7, 2.f / 3);
     path.dumpHex();
-    copy.setFillType(SkPath::kWinding_FillType);
+    copy.setFillType(SkPathFillType::kWinding);
     copy.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     copy.lineTo(SkBits2Float(0x3f5b6db7), SkBits2Float(0x3f2aaaab));  // 0.857143f, 0.666667f
     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
diff --git a/docs/examples/Path_dump_2.cpp b/docs/examples/Path_dump_2.cpp
index 37defe2..7220d0c 100644
--- a/docs/examples/Path_dump_2.cpp
+++ b/docs/examples/Path_dump_2.cpp
@@ -7,7 +7,7 @@
     SkPath path, copy;
     path.lineTo(6.f / 7, 2.f / 3);
     path.dump();
-    copy.setFillType(SkPath::kWinding_FillType);
+    copy.setFillType(SkPathFillType::kWinding);
     copy.moveTo(0, 0);
     copy.lineTo(0.857143f, 0.666667f);
     SkDebugf("path is " "%s" "equal to copy\n", path == copy ? "" : "not ");
diff --git a/docs/examples/Path_getConvexity.cpp b/docs/examples/Path_getConvexity.cpp
index ef09116..9f5987b 100644
--- a/docs/examples/Path_getConvexity.cpp
+++ b/docs/examples/Path_getConvexity.cpp
@@ -6,8 +6,8 @@
 void draw(SkCanvas* canvas) {
     auto debugster = [](const char* prefix, const SkPath& path) -> void {
         SkDebugf("%s path convexity is %s\n", prefix,
-                SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
-                SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
+                SkPathConvexityType::kUnknown == path.getConvexityType() ? "unknown" :
+                SkPathConvexityType::kConvex == path.getConvexityType() ? "convex" : "concave"); };
     SkPath path;
     debugster("initial", path);
     path.lineTo(50, 0);
diff --git a/docs/examples/Path_getConvexityOrUnknown.cpp b/docs/examples/Path_getConvexityOrUnknown.cpp
index b03c8b4..4aca799 100644
--- a/docs/examples/Path_getConvexityOrUnknown.cpp
+++ b/docs/examples/Path_getConvexityOrUnknown.cpp
@@ -6,17 +6,17 @@
 void draw(SkCanvas* canvas) {
     auto debugster = [](const char* prefix, const SkPath& path) -> void {
         SkDebugf("%s path convexity is %s\n", prefix,
-            SkPath::kUnknown_Convexity == path.getConvexityOrUnknown() ? "unknown" :
-            SkPath::kConvex_Convexity == path.getConvexityOrUnknown() ? "convex" : "concave"); };
+            SkPathConvexityType::kUnknown == path.getConvexityTypeOrUnknown() ? "unknown" :
+            SkPathConvexityType::kConvex  == path.getConvexityTypeOrUnknown() ? "convex" : "concave"); };
     SkPath path;
     debugster("initial", path);
     path.lineTo(50, 0);
     debugster("first line", path);
-    path.getConvexity();
+    path.getConvexityType();
     path.lineTo(50, 50);
     debugster("second line", path);
     path.lineTo(100, 50);
-    path.getConvexity();
+    path.getConvexityType();
     debugster("third line", path);
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_getFillType.cpp b/docs/examples/Path_getFillType.cpp
index 6ac58d1..f4e1cc7 100644
--- a/docs/examples/Path_getFillType.cpp
+++ b/docs/examples/Path_getFillType.cpp
@@ -6,9 +6,9 @@
 void draw(SkCanvas* canvas) {
     SkPath path;
     SkDebugf("default path fill type is %s\n",
-            path.getFillType() == SkPath::kWinding_FillType ? "kWinding_FillType" :
-            path.getFillType() == SkPath::kEvenOdd_FillType ? "kEvenOdd_FillType" :
-            path.getFillType() == SkPath::kInverseWinding_FillType ? "kInverseWinding_FillType" :
-                                                                     "kInverseEvenOdd_FillType");
+            path.getNewFillType() == SkPathFillType::kWinding ? "kWinding" :
+            path.getNewFillType() == SkPathFillType::kEvenOdd ? "kEvenOdd" :
+            path.getNewFillType() == SkPathFillType::kInverseWinding ? "kInverseWinding" :
+                                                                    "kInverseEvenOdd");
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_isRect.cpp b/docs/examples/Path_isRect.cpp
index 9d9a621..c099bb9 100644
--- a/docs/examples/Path_isRect.cpp
+++ b/docs/examples/Path_isRect.cpp
@@ -6,12 +6,12 @@
 void draw(SkCanvas* canvas) {
     auto debugster = [](const char* prefix, const SkPath& path) -> void {
         SkRect rect;
-        SkPath::Direction direction;
+        SkPathDirection direction;
         bool isClosed;
         path.isRect(&rect, &isClosed, &direction) ?
                 SkDebugf("%s is rect (%g, %g, %g, %g); is %s" "closed; direction %s\n", prefix,
                          rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, isClosed ? "" : "not ",
-                         SkPath::kCW_Direction == direction ? "CW" : "CCW") :
+                         SkPathDirection::kCW == direction ? "CW" : "CCW") :
                 SkDebugf("%s is not rect\n", prefix);
     };
     SkPath path;
diff --git a/docs/examples/Path_notequal_operator.cpp b/docs/examples/Path_notequal_operator.cpp
index dfba8c2..dcd56ef 100644
--- a/docs/examples/Path_notequal_operator.cpp
+++ b/docs/examples/Path_notequal_operator.cpp
@@ -13,8 +13,8 @@
     one.addRect({10, 20, 30, 40});
     two.addRect({10, 20, 30, 40});
     debugster("add rect", one, two);
-    one.setConvexity(SkPath::kConcave_Convexity);
+    one.setConvexityType(SkPathConvexityType::kConcave);
     debugster("setConvexity", one, two);
-    SkDebugf("convexity %c=\n", one.getConvexity() == two.getConvexity() ? '=' : '!');
+    SkDebugf("convexity %c=\n", one.getConvexityType() == two.getConvexityType() ? '=' : '!');
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_rArcTo.cpp b/docs/examples/Path_rArcTo.cpp
index 8244674..8eccb5b 100644
--- a/docs/examples/Path_rArcTo.cpp
+++ b/docs/examples/Path_rArcTo.cpp
@@ -9,7 +9,7 @@
     const SkPoint starts[] = {{20, 20}, {120, 20}, {70, 60}};
     for (auto start : starts) {
         path.moveTo(start.fX, start.fY);
-        path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPath::kCCW_Direction, 60, 0);
+        path.rArcTo(20, 20, 0, SkPath::kSmall_ArcSize, SkPathDirection::kCCW, 60, 0);
     }
     canvas->drawPath(path, paint);
 }
diff --git a/docs/examples/Path_rMoveTo.cpp b/docs/examples/Path_rMoveTo.cpp
index fd4e06d..f59b579 100644
--- a/docs/examples/Path_rMoveTo.cpp
+++ b/docs/examples/Path_rMoveTo.cpp
@@ -6,7 +6,7 @@
 REG_FIDDLE(Path_rMoveTo, 256, 100, false, 0) {
 void draw(SkCanvas* canvas) {
     SkPath path;
-    path.addRect({20, 20, 80, 80}, SkPath::kCW_Direction, 2);
+    path.addRect({20, 20, 80, 80}, SkPathDirection::kCW, 2);
     path.rMoveTo(25, 2);
     SkVector arrow[] = {{0, -4}, {-20, 0}, {0, -3}, {-5, 5}, {5, 5}, {0, -3}, {20, 0}};
     for (unsigned i = 0; i < SK_ARRAY_COUNT(arrow); ++i) {
diff --git a/docs/examples/Path_reset.cpp b/docs/examples/Path_reset.cpp
index 8448db1..b720d33 100644
--- a/docs/examples/Path_reset.cpp
+++ b/docs/examples/Path_reset.cpp
@@ -5,7 +5,7 @@
 REG_FIDDLE(Path_reset, 256, 256, true, 0) {
 void draw(SkCanvas* canvas) {
     SkPath path1, path2;
-    path1.setFillType(SkPath::kInverseWinding_FillType);
+    path1.setFillType(SkPathFillType::kInverseWinding);
     path1.addRect({10, 20, 30, 40});
     SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
     path1.reset();
diff --git a/docs/examples/Path_rewind.cpp b/docs/examples/Path_rewind.cpp
index 6776191..74a2683 100644
--- a/docs/examples/Path_rewind.cpp
+++ b/docs/examples/Path_rewind.cpp
@@ -5,7 +5,7 @@
 REG_FIDDLE(Path_rewind, 256, 256, true, 0) {
 void draw(SkCanvas* canvas) {
     SkPath path1, path2;
-    path1.setFillType(SkPath::kInverseWinding_FillType);
+    path1.setFillType(SkPathFillType::kInverseWinding);
     path1.addRect({10, 20, 30, 40});
     SkDebugf("path1 %c= path2\n", path1 == path2 ? '=' : '!');
     path1.rewind();
diff --git a/docs/examples/Path_setConvexity.cpp b/docs/examples/Path_setConvexity.cpp
index 16e5c9f..7fe5cee 100644
--- a/docs/examples/Path_setConvexity.cpp
+++ b/docs/examples/Path_setConvexity.cpp
@@ -6,15 +6,15 @@
 void draw(SkCanvas* canvas) {
     auto debugster = [](const char* prefix, const SkPath& path) -> void {
         SkDebugf("%s path convexity is %s\n", prefix,
-                SkPath::kUnknown_Convexity == path.getConvexity() ? "unknown" :
-                SkPath::kConvex_Convexity == path.getConvexity() ? "convex" : "concave"); };
+                SkPathConvexityType::kUnknown == path.getConvexityTypeOrUnknown() ? "unknown" :
+                SkPathConvexityType::kConvex == path.getConvexityTypeOrUnknown() ? "convex" : "concave"); };
         SkPoint quad[] = {{70, 70}, {20, 20}, {120, 20}, {120, 120}};
         SkPath path;
         path.addPoly(quad, SK_ARRAY_COUNT(quad), true);
         debugster("initial", path);
-        path.setConvexity(SkPath::kConcave_Convexity);
+        path.setConvexityType(SkPathConvexityType::kConcave);
         debugster("after forcing concave", path);
-        path.setConvexity(SkPath::kUnknown_Convexity);
+        path.setConvexityType(SkPathConvexityType::kUnknown);
         debugster("after forcing unknown", path);
 }
 }  // END FIDDLE
diff --git a/docs/examples/Path_setFillType.cpp b/docs/examples/Path_setFillType.cpp
index 0bed8a8..937fb69 100644
--- a/docs/examples/Path_setFillType.cpp
+++ b/docs/examples/Path_setFillType.cpp
@@ -5,7 +5,7 @@
 REG_FIDDLE(Path_setFillType, 256, 64, false, 0) {
 void draw(SkCanvas* canvas) {
     SkPath path;
-    path.setFillType(SkPath::kInverseWinding_FillType);
+    path.setFillType(SkPathFillType::kInverseWinding);
     SkPaint paint;
     paint.setColor(SK_ColorBLUE);
     canvas->drawPath(path, paint);
diff --git a/docs/examples/RRect_setRectRadii.cpp b/docs/examples/RRect_setRectRadii.cpp
index 2004e5f..86f35c7 100644
--- a/docs/examples/RRect_setRectRadii.cpp
+++ b/docs/examples/RRect_setRectRadii.cpp
@@ -15,10 +15,10 @@
     SkRRect rrect;
     SkVector corners[] = {{15, 17}, {17, 19}, {19, 15}, {15, 15}};
     rrect.setRectRadii({20, 20, 100, 100}, corners);
-    path.addRRect(rrect, SkPath::kCW_Direction);
+    path.addRRect(rrect, SkPathDirection::kCW);
     canvas->drawPath(path, paint);
     path.rewind();
-    path.addRRect(rrect, SkPath::kCCW_Direction, 1);
+    path.addRRect(rrect, SkPathDirection::kCCW, 1);
     canvas->translate(120, 0);
     canvas->drawPath(path, paint);
 }
diff --git a/docs/examples/Region_op_6.cpp b/docs/examples/Region_op_6.cpp
index 13c9205..9fbf850 100644
--- a/docs/examples/Region_op_6.cpp
+++ b/docs/examples/Region_op_6.cpp
@@ -9,9 +9,9 @@
     paint.setTextSize(128);
     SkPath xPath, opPath;
     paint.getTextPath("X", 1, 20, 110, &xPath);
-    xPath.setFillType(SkPath::kInverseWinding_FillType);
+    xPath.setFillType(SkPathFillType::kInverseWinding);
     opPath.addCircle(64, 64, frame * 64);
-    opPath.setFillType(SkPath::kInverseWinding_FillType);
+    opPath.setFillType(SkPathFillType::kInverseWinding);
     SkRegion xRegion, opRegion, rectRegion;
     SkIRect drawBounds = {0, 0, 128, 128};
     opRegion.setPath(opPath, SkRegion(drawBounds));
diff --git a/example/SkiaSDLExample.cpp b/example/SkiaSDLExample.cpp
index 7aebbbc..6dcee90 100644
--- a/example/SkiaSDLExample.cpp
+++ b/example/SkiaSDLExample.cpp
@@ -97,7 +97,7 @@
     for (int i = 0; i < kNumPoints; ++i) {
         concavePath.lineTo(points[(2 * i) % kNumPoints]);
     }
-    concavePath.setFillType(SkPath::kEvenOdd_FillType);
+    concavePath.setFillType(SkPathFillType::kEvenOdd);
     SkASSERT(!concavePath.isConvex());
     concavePath.close();
     return concavePath;
diff --git a/experimental/svg/model/SkSVGCircle.cpp b/experimental/svg/model/SkSVGCircle.cpp
index 66e7d28..47da074 100644
--- a/experimental/svg/model/SkSVGCircle.cpp
+++ b/experimental/svg/model/SkSVGCircle.cpp
@@ -54,7 +54,7 @@
     return std::make_tuple(SkPoint::Make(cx, cy), r);
 }
 void SkSVGCircle::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx,
-                         const SkPaint& paint, SkPath::FillType) const {
+                         const SkPaint& paint, SkPathFillType) const {
     SkPoint pos;
     SkScalar r;
     std::tie(pos, r) = this->resolve(lctx);
diff --git a/experimental/svg/model/SkSVGCircle.h b/experimental/svg/model/SkSVGCircle.h
index c53cf4a..fc774a1 100644
--- a/experimental/svg/model/SkSVGCircle.h
+++ b/experimental/svg/model/SkSVGCircle.h
@@ -26,7 +26,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGEllipse.cpp b/experimental/svg/model/SkSVGEllipse.cpp
index b7d4fad..60d3048 100644
--- a/experimental/svg/model/SkSVGEllipse.cpp
+++ b/experimental/svg/model/SkSVGEllipse.cpp
@@ -67,7 +67,7 @@
 }
 
 void SkSVGEllipse::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx,
-                          const SkPaint& paint, SkPath::FillType) const {
+                          const SkPaint& paint, SkPathFillType) const {
     canvas->drawOval(this->resolve(lctx), paint);
 }
 
diff --git a/experimental/svg/model/SkSVGEllipse.h b/experimental/svg/model/SkSVGEllipse.h
index c6bd2b3..ec0a753 100644
--- a/experimental/svg/model/SkSVGEllipse.h
+++ b/experimental/svg/model/SkSVGEllipse.h
@@ -27,7 +27,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGLine.cpp b/experimental/svg/model/SkSVGLine.cpp
index 3d0efa1..05fc1f9 100644
--- a/experimental/svg/model/SkSVGLine.cpp
+++ b/experimental/svg/model/SkSVGLine.cpp
@@ -64,7 +64,7 @@
 }
 
 void SkSVGLine::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx,
-                       const SkPaint& paint, SkPath::FillType) const {
+                       const SkPaint& paint, SkPathFillType) const {
     SkPoint p0, p1;
     std::tie(p0, p1) = this->resolve(lctx);
 
diff --git a/experimental/svg/model/SkSVGLine.h b/experimental/svg/model/SkSVGLine.h
index 08e0fe6..c4de847 100644
--- a/experimental/svg/model/SkSVGLine.h
+++ b/experimental/svg/model/SkSVGLine.h
@@ -27,7 +27,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGPath.cpp b/experimental/svg/model/SkSVGPath.cpp
index f3f4524..07fc348 100644
--- a/experimental/svg/model/SkSVGPath.cpp
+++ b/experimental/svg/model/SkSVGPath.cpp
@@ -26,7 +26,7 @@
 }
 
 void SkSVGPath::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint,
-                       SkPath::FillType fillType) const {
+                       SkPathFillType fillType) const {
     // the passed fillType follows inheritance rules and needs to be applied at draw time.
     fPath.setFillType(fillType);
     canvas->drawPath(fPath, paint);
diff --git a/experimental/svg/model/SkSVGPath.h b/experimental/svg/model/SkSVGPath.h
index 5b41637..1b7b17d 100644
--- a/experimental/svg/model/SkSVGPath.h
+++ b/experimental/svg/model/SkSVGPath.h
@@ -22,7 +22,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGPoly.cpp b/experimental/svg/model/SkSVGPoly.cpp
index 6bacb1a..29af890 100644
--- a/experimental/svg/model/SkSVGPoly.cpp
+++ b/experimental/svg/model/SkSVGPoly.cpp
@@ -33,7 +33,7 @@
 }
 
 void SkSVGPoly::onDraw(SkCanvas* canvas, const SkSVGLengthContext&, const SkPaint& paint,
-                       SkPath::FillType fillType) const {
+                       SkPathFillType fillType) const {
     // the passed fillType follows inheritance rules and needs to be applied at draw time.
     fPath.setFillType(fillType);
     canvas->drawPath(fPath, paint);
diff --git a/experimental/svg/model/SkSVGPoly.h b/experimental/svg/model/SkSVGPoly.h
index f47b764..ef9c3a5 100644
--- a/experimental/svg/model/SkSVGPoly.h
+++ b/experimental/svg/model/SkSVGPoly.h
@@ -30,7 +30,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGRect.cpp b/experimental/svg/model/SkSVGRect.cpp
index 24267d0..5c518bd 100644
--- a/experimental/svg/model/SkSVGRect.cpp
+++ b/experimental/svg/model/SkSVGRect.cpp
@@ -83,7 +83,7 @@
 }
 
 void SkSVGRect::onDraw(SkCanvas* canvas, const SkSVGLengthContext& lctx,
-                       const SkPaint& paint, SkPath::FillType) const {
+                       const SkPaint& paint, SkPathFillType) const {
     canvas->drawRRect(this->resolve(lctx), paint);
 }
 
diff --git a/experimental/svg/model/SkSVGRect.h b/experimental/svg/model/SkSVGRect.h
index 7d749b0..fca15e0 100644
--- a/experimental/svg/model/SkSVGRect.h
+++ b/experimental/svg/model/SkSVGRect.h
@@ -29,7 +29,7 @@
     void onSetAttribute(SkSVGAttribute, const SkSVGValue&) override;
 
     void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                SkPath::FillType) const override;
+                SkPathFillType) const override;
 
     SkPath onAsPath(const SkSVGRenderContext&) const override;
 
diff --git a/experimental/svg/model/SkSVGShape.h b/experimental/svg/model/SkSVGShape.h
index bfbad0e..ff57d5b 100644
--- a/experimental/svg/model/SkSVGShape.h
+++ b/experimental/svg/model/SkSVGShape.h
@@ -26,7 +26,7 @@
     void onRender(const SkSVGRenderContext&) const final;
 
     virtual void onDraw(SkCanvas*, const SkSVGLengthContext&, const SkPaint&,
-                        SkPath::FillType) const = 0;
+                        SkPathFillType) const = 0;
 
 private:
     typedef SkSVGTransformableNode INHERITED;
diff --git a/experimental/svg/model/SkSVGTypes.h b/experimental/svg/model/SkSVGTypes.h
index 9ac7654..00a09bf 100644
--- a/experimental/svg/model/SkSVGTypes.h
+++ b/experimental/svg/model/SkSVGTypes.h
@@ -239,9 +239,9 @@
 
     Type type() const { return fType; }
 
-    SkPath::FillType asFillType() const {
+    SkPathFillType asFillType() const {
         SkASSERT(fType != Type::kInherit); // should never be called for unresolved values.
-        return fType == Type::kEvenOdd ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
+        return fType == Type::kEvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding;
     }
 
 private:
diff --git a/fuzz/FuzzCommon.cpp b/fuzz/FuzzCommon.cpp
index 3a2108e..5478cc0 100644
--- a/fuzz/FuzzCommon.cpp
+++ b/fuzz/FuzzCommon.cpp
@@ -33,8 +33,8 @@
         return;
     }
     uint8_t fillType;
-    fuzz->nextRange(&fillType, 0, (uint8_t)SkPath::kInverseEvenOdd_FillType);
-    path->setFillType((SkPath::FillType)fillType);
+    fuzz->nextRange(&fillType, 0, (uint8_t)SkPathFillType::kInverseEvenOdd);
+    path->setFillType((SkPathFillType)fillType);
     uint8_t numOps;
     fuzz->nextRange(&numOps, 0, maxOps);
     for (uint8_t i = 0; i < numOps; ++i) {
@@ -52,7 +52,7 @@
         SkMatrix m;
         SkRRect rr;
         SkRect r;
-        SkPath::Direction dir;
+        SkPathDirection dir;
         unsigned int ui;
         SkScalar a, b, c, d, e, f;
         switch (op) {
@@ -112,32 +112,32 @@
             case 13:
                 fuzz_nice_rect(fuzz, &r);
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 path->addRect(r, dir);
                 break;
             case 14:
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 fuzz_nice_rect(fuzz, &r);
                 fuzz->next(&ui);
                 path->addRect(r, dir, ui);
                 break;
             case 15:
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 fuzz_nice_rect(fuzz, &r);
                 path->addOval(r, dir);
                 break;
             case 16:
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 fuzz_nice_rect(fuzz, &r);
                 fuzz->next(&ui);
                 path->addOval(r, dir, ui);
                 break;
             case 17:
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 fuzz_nice_float(fuzz, &a, &b, &c);
                 path->addCircle(a, b, c, dir);
                 break;
@@ -150,18 +150,18 @@
                 fuzz_nice_float(fuzz, &a, &b);
                 fuzz_nice_rect(fuzz, &r);
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 path->addRoundRect(r, a, b, dir);
                 break;
             case 20:
                 FuzzNiceRRect(fuzz, &rr);
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 path->addRRect(rr, dir);
                 break;
             case 21:
                 fuzz->nextRange(&ui, 0, 1);
-                dir = static_cast<SkPath::Direction>(ui);
+                dir = static_cast<SkPathDirection>(ui);
                 FuzzNiceRRect(fuzz, &rr);
                 path->addRRect(rr, dir, ui);
                 break;
diff --git a/fuzz/FuzzPathop.cpp b/fuzz/FuzzPathop.cpp
index 63862a9..30dc2a9 100644
--- a/fuzz/FuzzPathop.cpp
+++ b/fuzz/FuzzPathop.cpp
@@ -25,8 +25,8 @@
             for (uint8_t i = 0; i < ops && !fuzz->exhausted(); i++) {
                 SkPath path;
                 FuzzEvilPath(fuzz, &path, SkPath::Verb::kDone_Verb);
-                SkPath::FillType ft;
-                fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+                SkPathFillType ft;
+                fuzz->nextRange(&ft, 0, (int)SkPathFillType::kInverseEvenOdd);
                 path.setFillType(ft);
 
                 SkPathOp op;
@@ -41,8 +41,8 @@
         case 1: {
             SkPath path;
             FuzzEvilPath(fuzz, &path, SkPath::Verb::kDone_Verb);
-            SkPath::FillType ft;
-            fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+            SkPathFillType ft;
+            fuzz->nextRange(&ft, 0, (int)SkPathFillType::kInverseEvenOdd);
             path.setFillType(ft);
 
             SkPath result;
@@ -57,13 +57,13 @@
         case 2: {
             SkPath path;
             FuzzEvilPath(fuzz, &path, SkPath::Verb::kDone_Verb);
-            SkPath::FillType ft;
-            fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+            SkPathFillType ft;
+            fuzz->nextRange(&ft, 0, SkPathFillType::kInverseEvenOdd);
             path.setFillType(ft);
 
             SkPath path2;
             FuzzEvilPath(fuzz, &path2, SkPath::Verb::kDone_Verb);
-            fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+            fuzz->nextRange(&ft, 0, SkPathFillType::kInverseEvenOdd);
             path.setFillType(ft);
 
             SkPathOp op;
@@ -83,8 +83,8 @@
         case 3: {
             SkPath path;
             FuzzEvilPath(fuzz, &path, SkPath::Verb::kDone_Verb);
-            SkPath::FillType ft;
-            fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+            SkPathFillType ft;
+            fuzz->nextRange(&ft, 0, SkPathFillType::kInverseEvenOdd);
             path.setFillType(ft);
 
             SkPath result;
@@ -99,8 +99,8 @@
         case 4: {
             SkPath path;
             FuzzEvilPath(fuzz, &path, SkPath::Verb::kDone_Verb);
-            SkPath::FillType ft;
-            fuzz->nextRange(&ft, 0, SkPath::kInverseEvenOdd_FillType);
+            SkPathFillType ft;
+            fuzz->nextRange(&ft, 0, SkPathFillType::kInverseEvenOdd);
             path.setFillType(ft);
 
             SkRect result;
diff --git a/gm/aaa.cpp b/gm/aaa.cpp
index d056b0b..00f4474 100644
--- a/gm/aaa.cpp
+++ b/gm/aaa.cpp
@@ -64,7 +64,7 @@
                 SkBits2Float(0x4344f079), SkBits2Float(0x4397e900), SkBits2Float(0x3f3504f3));
         path.close();
         // Manually setting convexity is required. Otherwise, this path will be considered concave.
-        path.setConvexity(SkPath::kConvex_Convexity);
+        path.setConvexityType(SkPathConvexityType::kConvex);
         canvas->drawPath(path, p);
 
         // skbug.com/7573
@@ -143,7 +143,7 @@
 
         SkPath path;
         path.addCircle(100, 100, 30);
-        path.setFillType(SkPath::kInverseWinding_FillType);
+        path.setFillType(SkPathFillType::kInverseWinding);
         canvas->drawPath(path, p);
         canvas->restore();
 }
diff --git a/gm/arcto.cpp b/gm/arcto.cpp
index f28d86c..03942fe 100644
--- a/gm/arcto.cpp
+++ b/gm/arcto.cpp
@@ -79,13 +79,13 @@
             SkScalar ovalHeight = oval.height() / oHeight;
             svgArc.moveTo(oval.fLeft, oval.fTop);
             svgArc.arcTo(oval.width() / 2, ovalHeight, SkIntToScalar(angle), SkPath::kSmall_ArcSize,
-                    SkPath::kCW_Direction, oval.right(), oval.bottom());
+                    SkPathDirection::kCW, oval.right(), oval.bottom());
             canvas->drawPath(svgArc, paint);
             svgArc.reset();
 
             svgArc.moveTo(oval.fLeft + 100, oval.fTop + 100);
             svgArc.arcTo(oval.width() / 2, ovalHeight, SkIntToScalar(angle), SkPath::kLarge_ArcSize,
-                    SkPath::kCCW_Direction, oval.right(), oval.bottom() + 100);
+                    SkPathDirection::kCCW, oval.right(), oval.bottom() + 100);
             canvas->drawPath(svgArc, paint);
             oval.offset(50, 0);
             svgArc.reset();
@@ -114,12 +114,12 @@
     paint.setStrokeCap(SkPaint::kRound_Cap);
     SkPath path;
     path.moveTo(100, 100);
-    path.arcTo(0, 0, 0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 200, 200);
+    path.arcTo(0, 0, 0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 200, 200);
     canvas->drawPath(path, paint);
 
     path.reset();
     path.moveTo(200, 100);
-    path.arcTo(80, 80, 0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 200, 100);
+    path.arcTo(80, 80, 0, SkPath::kLarge_ArcSize, SkPathDirection::kCW, 200, 100);
     canvas->drawPath(path, paint);
 }
 
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index 70bcf77..d944219 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -71,12 +71,11 @@
     }
 
 protected:
-    BezierTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, const SkPMColor4f& color,
-                 int32_t classID)
+    BezierTestOp(GrClipEdgeType et, const SkRect& rect, const SkPMColor4f& color, int32_t classID)
             : INHERITED(classID)
             , fRect(rect)
             , fColor(color)
-            , fGeometryProcessor(std::move(gp))
+            , fEdgeType(et)
             , fProcessorSet(SkBlendMode::kSrc) {
         this->setBounds(rect, HasAABloat::kYes, IsHairline::kNo);
     }
@@ -86,7 +85,7 @@
                 this, chainBounds, std::move(fProcessorSet));
     }
 
-    sk_sp<const GrGeometryProcessor> gp() const { return fGeometryProcessor; }
+    GrClipEdgeType edgeType() const { return fEdgeType; }
 
     const SkRect& rect() const { return fRect; }
     const SkPMColor4f& color() const { return fColor; }
@@ -94,7 +93,7 @@
 private:
     SkRect fRect;
     SkPMColor4f fColor;
-    sk_sp<const GrGeometryProcessor> fGeometryProcessor;
+    GrClipEdgeType fEdgeType;
     GrProcessorSet fProcessorSet;
 
     typedef GrMeshDrawOp INHERITED;
@@ -103,7 +102,6 @@
 /**
  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
  */
-
 class BezierConicTestOp : public BezierTestOp {
 public:
     DEFINE_OP_CLASS_ID
@@ -111,21 +109,21 @@
     const char* name() const override { return "BezierConicTestOp"; }
 
     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                                          sk_sp<const GrGeometryProcessor> gp,
+                                          GrClipEdgeType et,
                                           const SkRect& rect,
                                           const SkPMColor4f& color,
                                           const SkMatrix& klm) {
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
 
-        return pool->allocate<BezierConicTestOp>(std::move(gp), rect, color, klm);
+        return pool->allocate<BezierConicTestOp>(et, rect, color, klm);
     }
 
 private:
     friend class ::GrOpMemoryPool; // for ctor
 
-    BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
+    BezierConicTestOp(GrClipEdgeType et, const SkRect& rect,
                       const SkPMColor4f& color, const SkMatrix& klm)
-            : INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {}
+            : INHERITED(et, rect, color, ClassID()), fKLM(klm) {}
 
     struct Vertex {
         SkPoint fPosition;
@@ -133,7 +131,14 @@
     };
 
     void onPrepareDraws(Target* target) override {
-        SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
+        GrGeometryProcessor* gp = GrConicEffect::Make(target->allocator(), this->color(),
+                                                      SkMatrix::I(), this->edgeType(),
+                                                      target->caps(), SkMatrix::I(), false);
+        if (!gp) {
+            return;
+        }
+
+        SkASSERT(gp->vertexStride() == sizeof(Vertex));
         QuadHelper helper(target, sizeof(Vertex), 1);
         Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
         if (!verts) {
@@ -147,7 +152,7 @@
             fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1);
         }
 
-        helper.recordDraw(target, this->gp());
+        helper.recordDraw(target, gp);
     }
 
     SkMatrix fKLM;
@@ -169,76 +174,78 @@
     }
 
 protected:
+    static const int kNumConics = 10;
+    static const int kCellWidth = 128;
+    static const int kCellHeight = 128;
+
     SkString onShortName() override {
         return SkString("bezier_conic_effects");
     }
 
     SkISize onISize() override {
-        return SkISize::Make(800, 800);
+        return SkISize::Make(kGrClipEdgeTypeCnt*kCellWidth, kNumConics*kCellHeight);
     }
 
-
     void onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                 SkCanvas* canvas) override {
-        struct Vertex {
-            SkPoint fPosition;
-            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
+
+        const SkScalar w = kCellWidth, h = kCellHeight;
+        const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000);
+
+        const SkPoint baseControlPts[kNumConics][3] = {
+            { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } },
+            { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } },
+            { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } },
+            { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } },
+            { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } },
+            { { 0.96f * w, 0.65f * h}, { 0.03f * w, 0.79f * h }, { 0.24f * w, 0.56f * h } },
+            { { 0.57f * w, 0.12f * h}, { 0.33f * w, 0.67f * h }, { 0.59f * w, 0.33f * h } },
+            { { 0.12f * w, 0.72f * h}, { 0.69f * w, 0.85f * h }, { 0.46f * w, 0.32f * h } },
+            { { 0.27f * w, 0.49f * h}, { 0.41f * w, 0.02f * h }, { 0.11f * w, 0.42f * h } },
+            { { 0.40f * w, 0.13f * h}, { 0.83f * w, 0.30f * h }, { 0.31f * w, 0.68f * h } },
         };
+        const SkScalar weights[kNumConics] = { 0.62f, 0.01f, 0.95f, 1.48f, 0.37f,
+                                               0.66f, 0.15f, 0.14f, 0.61f, 1.4f };
 
-        constexpr int kNumConics = 10;
-        SkRandom rand;
+        SkPaint ctrlPtPaint;
+        ctrlPtPaint.setColor(SK_ColorRED);
 
-        // Mult by 3 for each edge effect type
-        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
-        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
-        SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
-        SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
-        int row = 0;
-        int col = 0;
-        SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
+        SkPaint choppedPtPaint;
+        choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
 
-        for (int i = 0; i < kNumConics; ++i) {
-            SkPoint baseControlPts[] = {
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
-            };
-            SkScalar weight = rand.nextRangeF(0.f, 2.f);
-            for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
-                sk_sp<GrGeometryProcessor> gp;
-                GrClipEdgeType et = (GrClipEdgeType)edgeType;
-                gp = GrConicEffect::Make(color, SkMatrix::I(), et, *context->priv().caps(),
-                                         SkMatrix::I(), false);
-                if (!gp) {
-                    continue;
-                }
+        SkPaint polyPaint;
+        polyPaint.setColor(0xffA0A0A0);
+        polyPaint.setStrokeWidth(0);
+        polyPaint.setStyle(SkPaint::kStroke_Style);
+
+        SkPaint boundsPaint;
+        boundsPaint.setColor(0xff808080);
+        boundsPaint.setStrokeWidth(0);
+        boundsPaint.setStyle(SkPaint::kStroke_Style);
+
+
+        for (int row = 0; row < kNumConics; ++row) {
+            for(int col = 0; col < kGrClipEdgeTypeCnt; ++col) {
+                GrClipEdgeType et = (GrClipEdgeType) col;
 
                 SkScalar x = col * w;
                 SkScalar y = row * h;
                 SkPoint controlPts[] = {
-                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
-                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
-                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
+                    {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY},
+                    {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY},
+                    {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY}
                 };
-                SkConic dst[4];
-                SkMatrix klm;
-                int cnt = chop_conic(controlPts, dst, weight);
-                GrPathUtils::getConicKLM(controlPts, weight, &klm);
 
-                SkPaint ctrlPtPaint;
-                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
                 for (int i = 0; i < 3; ++i) {
                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
                 }
 
-                SkPaint polyPaint;
-                polyPaint.setColor(0xffA0A0A0);
-                polyPaint.setStrokeWidth(0);
-                polyPaint.setStyle(SkPaint::kStroke_Style);
                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
 
-                SkPaint choppedPtPaint;
-                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
+                SkConic dst[4];
+                SkMatrix klm;
+                int cnt = ChopConic(controlPts, dst, weights[row]);
+                GrPathUtils::getConicKLM(controlPts, weights[row], &klm);
 
                 for (int c = 0; c < cnt; ++c) {
                     SkPoint* pts = dst[c].fPts;
@@ -247,25 +254,14 @@
                     }
 
                     SkRect bounds;
-                    //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
-                    //bounds.set(bPts, 2);
                     bounds.setBounds(pts, 3);
 
-                    SkPaint boundsPaint;
-                    boundsPaint.setColor(0xff808080);
-                    boundsPaint.setStrokeWidth(0);
-                    boundsPaint.setStyle(SkPaint::kStroke_Style);
                     canvas->drawRect(bounds, boundsPaint);
 
-                    std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, gp, bounds,
-                                                                           color, klm);
+                    std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, et, bounds,
+                                                                           kOpaqueBlack, klm);
                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
                 }
-                ++col;
-                if (numCols == col) {
-                    col = 0;
-                    ++row;
-                }
             }
         }
     }
@@ -276,7 +272,7 @@
     // found along the curve segment it will return 1 and
     // dst[0] is the original conic. If it returns 2 the dst[0]
     // and dst[1] are the two new conics.
-    int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
+    static int SplitConic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
         SkScalar t = SkFindQuadMaxCurvature(src);
         if (t == 0 || t == 1) {
             if (dst) {
@@ -296,15 +292,15 @@
         }
     }
 
-    // Calls split_conic on the entire conic and then once more on each subsection.
+    // Calls SplitConic on the entire conic and then once more on each subsection.
     // Most cases will result in either 1 conic (chop point is not within t range)
     // or 3 points (split once and then one subsection is split again).
-    int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
+    static int ChopConic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
         SkConic dstTemp[2];
-        int conicCnt = split_conic(src, dstTemp, weight);
+        int conicCnt = SplitConic(src, dstTemp, weight);
         if (2 == conicCnt) {
-            int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
-            conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
+            int conicCnt2 = SplitConic(dstTemp[0].fPts, dst, dstTemp[0].fW);
+            conicCnt = conicCnt2 + SplitConic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
         } else {
             dst[0] = dstTemp[0];
         }
@@ -322,21 +318,21 @@
     const char* name() const override { return "BezierQuadTestOp"; }
 
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
-                                          sk_sp<const GrGeometryProcessor> gp,
+                                          GrClipEdgeType et,
                                           const SkRect& rect,
                                           const SkPMColor4f& color,
                                           const GrPathUtils::QuadUVMatrix& devToUV) {
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
 
-        return pool->allocate<BezierQuadTestOp>(std::move(gp), rect, color, devToUV);
+        return pool->allocate<BezierQuadTestOp>(et, rect, color, devToUV);
     }
 
 private:
     friend class ::GrOpMemoryPool; // for ctor
 
-    BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
+    BezierQuadTestOp(GrClipEdgeType et, const SkRect& rect,
                      const SkPMColor4f& color, const GrPathUtils::QuadUVMatrix& devToUV)
-            : INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {}
+            : INHERITED(et, rect, color, ClassID()), fDevToUV(devToUV) {}
 
     struct Vertex {
         SkPoint fPosition;
@@ -344,7 +340,14 @@
     };
 
     void onPrepareDraws(Target* target) override {
-        SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
+        GrGeometryProcessor* gp = GrQuadEffect::Make(target->allocator(), this->color(),
+                                                     SkMatrix::I(), this->edgeType(),
+                                                     target->caps(), SkMatrix::I(), false);
+        if (!gp) {
+            return;
+        }
+
+        SkASSERT(gp->vertexStride() == sizeof(Vertex));
         QuadHelper helper(target, sizeof(Vertex), 1);
         Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
         if (!verts) {
@@ -353,7 +356,7 @@
         SkRect rect = this->rect();
         SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex));
         fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint));
-        helper.recordDraw(target, this->gp());
+        helper.recordDraw(target, gp);
     }
 
     GrPathUtils::QuadUVMatrix fDevToUV;
@@ -374,72 +377,68 @@
     }
 
 protected:
+    static const int kNumQuads = 5;
+    static const int kCellWidth = 128;
+    static const int kCellHeight = 128;
+
     SkString onShortName() override {
         return SkString("bezier_quad_effects");
     }
 
     SkISize onISize() override {
-        return SkISize::Make(800, 800);
+        return SkISize::Make(kGrClipEdgeTypeCnt*kCellWidth, kNumQuads*kCellHeight);
     }
 
-
     void onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                 SkCanvas* canvas) override {
-        struct Vertex {
-            SkPoint fPosition;
-            float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
+
+        const SkScalar w = kCellWidth, h = kCellHeight;
+        const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000);
+
+        const SkPoint baseControlPts[kNumQuads][3] = {
+            { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } },
+            { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } },
+            { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } },
+            { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } },
+            { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } },
         };
 
-        constexpr int kNumQuads = 5;
-        SkRandom rand;
+        SkPaint ctrlPtPaint;
+        ctrlPtPaint.setColor(SK_ColorRED);
 
-        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
-        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
-        SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
-        SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
-        int row = 0;
-        int col = 0;
-        SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
+        SkPaint choppedPtPaint;
+        choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
 
-        for (int i = 0; i < kNumQuads; ++i) {
-            SkPoint baseControlPts[] = {
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
-                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
-            };
-            for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
-                sk_sp<GrGeometryProcessor> gp;
-                GrClipEdgeType et = (GrClipEdgeType)edgeType;
-                gp = GrQuadEffect::Make(color, SkMatrix::I(), et, *context->priv().caps(),
-                                        SkMatrix::I(), false);
-                if (!gp) {
-                    continue;
-                }
+        SkPaint polyPaint;
+        polyPaint.setColor(0xffA0A0A0);
+        polyPaint.setStrokeWidth(0);
+        polyPaint.setStyle(SkPaint::kStroke_Style);
+
+        SkPaint boundsPaint;
+        boundsPaint.setColor(0xff808080);
+        boundsPaint.setStrokeWidth(0);
+        boundsPaint.setStyle(SkPaint::kStroke_Style);
+
+        for (int row = 0; row < kNumQuads; ++row) {
+            for(int col = 0; col < kGrClipEdgeTypeCnt; ++col) {
+                GrClipEdgeType et = (GrClipEdgeType) col;
 
                 SkScalar x = col * w;
                 SkScalar y = row * h;
                 SkPoint controlPts[] = {
-                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
-                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
-                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
+                    {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY},
+                    {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY},
+                    {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY}
                 };
-                SkPoint chopped[5];
-                int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
 
-                SkPaint ctrlPtPaint;
-                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
                 for (int i = 0; i < 3; ++i) {
                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
                 }
 
-                SkPaint polyPaint;
-                polyPaint.setColor(0xffA0A0A0);
-                polyPaint.setStrokeWidth(0);
-                polyPaint.setStyle(SkPaint::kStroke_Style);
                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
 
-                SkPaint choppedPtPaint;
-                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
+                SkPoint chopped[5];
+                int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
 
                 for (int c = 0; c < cnt; ++c) {
                     SkPoint* pts = chopped + 2 * c;
@@ -451,26 +450,14 @@
                     SkRect bounds;
                     bounds.setBounds(pts, 3);
 
-                    SkPaint boundsPaint;
-                    boundsPaint.setColor(0xff808080);
-                    boundsPaint.setStrokeWidth(0);
-                    boundsPaint.setStyle(SkPaint::kStroke_Style);
                     canvas->drawRect(bounds, boundsPaint);
 
-                    GrPaint grPaint;
-                    grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
-
                     GrPathUtils::QuadUVMatrix DevToUV(pts);
 
-                    std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, gp,
-                                                                          bounds, color, DevToUV);
+                    std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, et, bounds,
+                                                                          kOpaqueBlack, DevToUV);
                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
                 }
-                ++col;
-                if (numCols == col) {
-                    col = 0;
-                    ++row;
-                }
             }
         }
     }
diff --git a/gm/bigblurs.cpp b/gm/bigblurs.cpp
index ff12b6b..6436c85 100644
--- a/gm/bigblurs.cpp
+++ b/gm/bigblurs.cpp
@@ -52,7 +52,7 @@
         SkPath rectori;
 
         rectori.addRect(bigRect);
-        rectori.addRect(insetRect, SkPath::kCCW_Direction);
+        rectori.addRect(insetRect, SkPathDirection::kCCW);
 
         // The blur extends 3*kSigma out from the big rect.
         // Offset the close-up windows so we get the entire blur
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
index 4d30c98..35bafc7 100644
--- a/gm/blurrect.cpp
+++ b/gm/blurrect.cpp
@@ -53,7 +53,7 @@
     rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
 
     path.addRect(rect);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     canvas->drawPath(path, p);
 }
@@ -71,7 +71,7 @@
     rect.offset(7, -7);
 
     path.addRect(rect);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     canvas->drawPath(path, p);
 }
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index 3aef017..e6f6910 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -89,8 +89,8 @@
         SkRect outer = SkRect::MakeXYWH(10.125f, 10.125f, 100.125f, 100);
         SkRect inner = SkRect::MakeXYWH(20.25f, 20.125f, 80, 80);
         SkPath path;
-        path.addRect(outer, SkPath::kCW_Direction);
-        path.addRect(inner, SkPath::kCCW_Direction);
+        path.addRect(outer, SkPathDirection::kCW);
+        path.addRect(inner, SkPathDirection::kCCW);
 
         canvas->drawPath(path, paint);
         // important to translate by a factional amount to exercise a different "phase"
@@ -107,8 +107,8 @@
         SkRect outer = SkRect::MakeXYWH(10, 110, 100, 100);
         SkRect inner = SkRect::MakeXYWH(50, 150, 10, 10);
         SkPath path;
-        path.addRect(outer, SkPath::kCW_Direction);
-        path.addRect(inner, SkPath::kCW_Direction);
+        path.addRect(outer, SkPathDirection::kCW);
+        path.addRect(inner, SkPathDirection::kCW);
         canvas->drawPath(path, paint);
 
         SkScalar dx = SkScalarRoundToScalar(path.getBounds().width()) + 40 + 0.25f;
diff --git a/gm/circularclips.cpp b/gm/circularclips.cpp
index 025b693..b213838 100644
--- a/gm/circularclips.cpp
+++ b/gm/circularclips.cpp
@@ -28,8 +28,8 @@
         fY = 50;
         fR = 40;
 
-        fCircle1.addCircle(fX1, fY, fR, SkPath::kCW_Direction);
-        fCircle2.addCircle(fX2, fY, fR, SkPath::kCW_Direction);
+        fCircle1.addCircle(fX1, fY, fR, SkPathDirection::kCW);
+        fCircle2.addCircle(fX2, fY, fR, SkPathDirection::kCW);
     }
 
 
diff --git a/gm/clockwise.cpp b/gm/clockwise.cpp
index f16e45a..c52cc3a 100644
--- a/gm/clockwise.cpp
+++ b/gm/clockwise.cpp
@@ -52,6 +52,7 @@
 #include "src/gpu/glsl/GrGLSLVarying.h"
 #include "src/gpu/ops/GrDrawOp.h"
 #include "src/gpu/ops/GrOp.h"
+#include "tools/gpu/ProxyUtils.h"
 
 #include <memory>
 #include <utility>
@@ -80,26 +81,37 @@
 
 class ClockwiseTestProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool readSkFragCoord) {
+        return arena->make<ClockwiseTestProcessor>(readSkFragCoord);
+    }
+
+    const char* name() const final { return "ClockwiseTestProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
+        b->add32(fReadSkFragCoord);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+    bool readSkFragCoord() const { return fReadSkFragCoord; }
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     ClockwiseTestProcessor(bool readSkFragCoord)
             : GrGeometryProcessor(kClockwiseTestProcessor_ClassID)
             , fReadSkFragCoord(readSkFragCoord) {
         this->setVertexAttributes(&gVertex, 1);
     }
-    const char* name() const override { return "ClockwiseTestProcessor"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
-        b->add32(fReadSkFragCoord);
-    }
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
 
-private:
     const bool fReadSkFragCoord;
 
-    friend class GLSLClockwiseTestProcessor;
+    typedef GrGeometryProcessor INHERITED;
 };
 
 class GLSLClockwiseTestProcessor : public GrGLSLGeometryProcessor {
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) override {}
+                 const CoordTransformRange&) override {}
 
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
         const ClockwiseTestProcessor& proc = args.fGP.cast<ClockwiseTestProcessor>();
@@ -107,7 +119,7 @@
         gpArgs->fPositionVar.set(kFloat2_GrSLType, "position");
         args.fFragBuilder->codeAppendf(
                 "%s = sk_Clockwise ? half4(0,1,0,1) : half4(1,0,0,1);", args.fOutputColor);
-        if (!proc.fReadSkFragCoord) {
+        if (!proc.readSkFragCoord()) {
             args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
         } else {
             // Verify layout(origin_upper_left) on gl_FragCoord does not affect gl_FrontFacing.
@@ -137,7 +149,9 @@
 
 private:
     ClockwiseTestOp(bool readSkFragCoord, float y)
-            : GrDrawOp(ClassID()), fReadSkFragCoord(readSkFragCoord), fY(y) {
+            : GrDrawOp(ClassID())
+            , fReadSkFragCoord(readSkFragCoord)
+            , fY(y) {
         this->setBounds(SkRect::MakeXYWH(0, fY, 100, 100), HasAABloat::kNo, IsHairline::kNo);
     }
 
@@ -147,6 +161,25 @@
                                       bool hasMixedSampledCoverage, GrClampType) override {
         return GrProcessorSet::EmptySetAnalysis();
     }
+
+    void onPrePrepare(GrRecordingContext* context,
+                      const GrSurfaceProxyView* dstView,
+                      GrAppliedClip* clip,
+                      const GrXferProcessor::DstProxyView& dstProxyView) final {
+        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
+
+        // This is equivalent to a GrOpFlushState::detachAppliedClip
+        GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
+
+        GrGeometryProcessor* geomProc = ClockwiseTestProcessor::Make(arena, fReadSkFragCoord);
+
+        // TODO: need to also give this to the recording context
+        fProgramInfo = sk_gpu_test::CreateProgramInfo(context->priv().caps(), arena, dstView,
+                                                      std::move(appliedClip), dstProxyView,
+                                                      geomProc, SkBlendMode::kPlus,
+                                                      GrPrimitiveType::kTriangleStrip);
+    }
+
     void onPrepare(GrOpFlushState* flushState) override {
         SkPoint vertices[4] = {
             {100, fY},
@@ -157,32 +190,47 @@
         fVertexBuffer = flushState->resourceProvider()->createBuffer(
                 sizeof(vertices), GrGpuBufferType::kVertex, kStatic_GrAccessPattern, vertices);
     }
+
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
         if (!fVertexBuffer) {
             return;
         }
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus,
-                            flushState->drawOpArgs().outputSwizzle());
+
+        if (!fProgramInfo) {
+            GrGeometryProcessor* geomProc = ClockwiseTestProcessor::Make(flushState->allocator(),
+                                                                         fReadSkFragCoord);
+
+            fProgramInfo = sk_gpu_test::CreateProgramInfo(&flushState->caps(),
+                                                          flushState->allocator(),
+                                                          flushState->view(),
+                                                          flushState->detachAppliedClip(),
+                                                          flushState->dstProxyView(),
+                                                          geomProc, SkBlendMode::kPlus,
+                                                          GrPrimitiveType::kTriangleStrip);
+        }
+
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
         mesh.setNonIndexedNonInstanced(4);
         mesh.setVertexData(std::move(fVertexBuffer));
 
-        ClockwiseTestProcessor primProc(fReadSkFragCoord);
-
-        GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                                  flushState->drawOpArgs().origin(),
-                                  pipeline,
-                                  primProc,
-                                  nullptr, nullptr, 0);
-
-        flushState->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::MakeXYWH(0, fY, 100, 100));
+        flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1,
+                                          SkRect::MakeXYWH(0, fY, 100, 100));
     }
 
     sk_sp<GrBuffer> fVertexBuffer;
-    const bool fReadSkFragCoord;
-    const float fY;
+    const bool      fReadSkFragCoord;
+    const float     fY;
+
+    // The program info (and both the GrPipeline and GrPrimitiveProcessor it relies on), when
+    // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
+    // arena's job to free up their memory so we just have a bare programInfo pointer here. We
+    // don't even store the GrPipeline and GrPrimitiveProcessor pointers here bc they are
+    // guaranteed to have the same lifetime as the program info.
+    GrProgramInfo*  fProgramInfo = nullptr;
 
     friend class ::GrOpMemoryPool; // for ctor
+
+    typedef GrDrawOp INHERITED;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -205,8 +253,8 @@
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
         rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()), rtcColorType,
-                         GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
-                         SK_PMColor4fWHITE, {0, 0, 100, 200},
+                         rtc->colorInfo().alphaType(), GrSamplerState::Filter::kNearest,
+                         SkBlendMode::kSrcOver, SK_PMColor4fWHITE, {0, 0, 100, 200},
                          {100, 0, 200, 200}, GrAA::kNo, GrQuadAAFlags::kNone,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
                          nullptr);
@@ -221,8 +269,8 @@
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
         rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()), rtcColorType,
-                         GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
-                         SK_PMColor4fWHITE, {0, 0, 100, 200},
+                         rtc->colorInfo().alphaType(), GrSamplerState::Filter::kNearest,
+                         SkBlendMode::kSrcOver, SK_PMColor4fWHITE, {0, 0, 100, 200},
                          {200, 0, 300, 200}, GrAA::kNo, GrQuadAAFlags::kNone,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
                          nullptr);
diff --git a/gm/collapsepaths.cpp b/gm/collapsepaths.cpp
index 7335171..82233c9 100644
--- a/gm/collapsepaths.cpp
+++ b/gm/collapsepaths.cpp
@@ -21,7 +21,7 @@
     path.moveTo(  370.50653076171875,   73.684051513671875);
     path.lineTo(  525.02093505859375, 208.6413726806640625);
     path.lineTo(    478.403564453125, 213.5998992919921875);
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
@@ -70,7 +70,7 @@
     path.moveTo(154.6182708740234375,  188.230926513671875);
     path.lineTo( 430.242095947265625,   546.76605224609375);
     path.lineTo(      373.1005859375,    559.0906982421875);
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
@@ -83,7 +83,7 @@
     path.moveTo(39.09362030029296875, 47.59223175048828125);
     path.lineTo(108.7822418212890625,  138.244110107421875);
     path.lineTo(94.33460235595703125,  141.360260009765625);
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
@@ -96,7 +96,7 @@
     path.moveTo(40.33458709716796875, 49.10297393798828125);
     path.lineTo(112.2353668212890625,    142.6324462890625);
     path.lineTo(97.32910919189453125, 145.8475189208984375);
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
@@ -109,7 +109,7 @@
     path.moveTo( 34.50,  42.00);
     path.lineTo( 96.00, 122.00);
     path.lineTo( 83.25, 124.75);
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
@@ -125,7 +125,7 @@
     path.lineTo(SkDoubleToScalar(100.167),  SkDoubleToScalar(140.096));
     path.lineTo(SkDoubleToScalar( 99.0364), SkDoubleToScalar(140.364));
     path.lineTo(SkDoubleToScalar( 94.25),   SkDoubleToScalar(141.50));
-    path.setFillType(SkPath::FillType::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     canvas->drawPath(path, paint);
 }
 
diff --git a/gm/colormatrix.cpp b/gm/colormatrix.cpp
index 806dea6..8f3f5e2 100644
--- a/gm/colormatrix.cpp
+++ b/gm/colormatrix.cpp
@@ -26,7 +26,7 @@
 #include "include/effects/SkGradientShader.h"
 
 #define WIDTH 500
-#define HEIGHT 500
+#define HEIGHT 160
 
 static void set_color_matrix(SkPaint* paint, const SkColorMatrix& matrix) {
     paint->setColorFilter(SkColorFilters::Matrix(matrix));
@@ -100,41 +100,25 @@
             set_color_matrix(&paint, matrix);
             canvas->drawImage(bmps[i], 0, 0, &paint);
 
-            matrix.setRotate(SkColorMatrix::kR_Axis, 90);
+            ///////////////////////////////////////////////
+
+            matrix.setSaturation(0.0f);
             set_color_matrix(&paint, matrix);
             canvas->drawImage(bmps[i], 80, 0, &paint);
 
-            matrix.setRotate(SkColorMatrix::kG_Axis, 90);
+            matrix.setSaturation(0.5f);
             set_color_matrix(&paint, matrix);
             canvas->drawImage(bmps[i], 160, 0, &paint);
 
-            matrix.setRotate(SkColorMatrix::kB_Axis, 90);
-            set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 240, 0, &paint);
-            ///////////////////////////////////////////////
-            matrix.setSaturation(0.0f);
-            set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 0, 80, &paint);
-
-            matrix.setSaturation(0.5f);
-            set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 80, 80, &paint);
-
             matrix.setSaturation(1.0f);
             set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 160, 80, &paint);
+            canvas->drawImage(bmps[i], 240, 0, &paint);
 
             matrix.setSaturation(2.0f);
             set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 240, 80, &paint);
-            ///////////////////////////////////////////////
-            matrix.setRGB2YUV();
-            set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 0, 160, &paint);
+            canvas->drawImage(bmps[i], 320, 0, &paint);
 
-            matrix.setYUV2RGB();
-            set_color_matrix(&paint, matrix);
-            canvas->drawImage(bmps[i], 80, 160, &paint);
+            ///////////////////////////////////////////////
 
             // Move red into alpha, set color to white
             float data[20] = {
@@ -145,9 +129,9 @@
             };
 
             set_array(&paint, data);
-            canvas->drawImage(bmps[i], 160, 160, &paint);
+            canvas->drawImage(bmps[i], 400, 0, &paint);
             ///////////////////////////////////////////////
-            canvas->translate(0, 240);
+            canvas->translate(0, 80);
         }
     }
 
diff --git a/gm/complexclip.cpp b/gm/complexclip.cpp
index 3e53783..944251a 100644
--- a/gm/complexclip.cpp
+++ b/gm/complexclip.cpp
@@ -68,9 +68,9 @@
             .lineTo(50,  150)
             .close();
         if (fInvertDraw) {
-            path.setFillType(SkPath::kInverseEvenOdd_FillType);
+            path.setFillType(SkPathFillType::kInverseEvenOdd);
         } else {
-            path.setFillType(SkPath::kEvenOdd_FillType);
+            path.setFillType(SkPathFillType::kEvenOdd);
         }
         SkPaint pathPaint;
         pathPaint.setAntiAlias(true);
@@ -126,10 +126,10 @@
                 bool doInvB = SkToBool(invBits & 2);
                 canvas->save();
                     // set clip
-                    clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
-                                      SkPath::kEvenOdd_FillType);
-                    clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
-                                      SkPath::kEvenOdd_FillType);
+                    clipA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
+                                      SkPathFillType::kEvenOdd);
+                    clipB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
+                                      SkPathFillType::kEvenOdd);
                     canvas->clipPath(clipA, fDoAAClip);
                     canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip);
 
diff --git a/gm/complexclip3.cpp b/gm/complexclip3.cpp
index f138ee0..b48c35d 100644
--- a/gm/complexclip3.cpp
+++ b/gm/complexclip3.cpp
@@ -96,10 +96,10 @@
                         bool doInvB = SkToBool(invB);
                         canvas->save();
                         // set clip
-                        firstClip->setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
-                                               SkPath::kEvenOdd_FillType);
-                        secondClip->setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
-                                                SkPath::kEvenOdd_FillType);
+                        firstClip->setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
+                                               SkPathFillType::kEvenOdd);
+                        secondClip->setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
+                                                SkPathFillType::kEvenOdd);
                         canvas->clipPath(*firstClip, doAAA);
                         canvas->clipPath(*secondClip, gOps[op].fOp, doAAB);
 
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
index 2fa2adf..6adb597 100644
--- a/gm/convex_all_line_paths.cpp
+++ b/gm/convex_all_line_paths.cpp
@@ -183,7 +183,7 @@
     SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
     bool runAsBench() const override { return true; }
 
-    static SkPath GetPath(int index, SkPath::Direction dir) {
+    static SkPath GetPath(int index, SkPathDirection dir) {
         std::unique_ptr<SkPoint[]> data(nullptr);
         const SkPoint* points;
         int numPts;
@@ -238,7 +238,7 @@
 
         SkPath path;
 
-        if (SkPath::kCW_Direction == dir) {
+        if (SkPathDirection::kCW == dir) {
             path.moveTo(points[0]);
             for (int i = 1; i < numPts; ++i) {
                 path.lineTo(points[i]);
@@ -273,7 +273,7 @@
 
         SkPoint center;
         {
-            SkPath path = GetPath(index, SkPath::kCW_Direction);
+            SkPath path = GetPath(index, SkPathDirection::kCW);
             if (offset->fX+path.getBounds().width() > kGMWidth) {
                 offset->fX = 0;
                 offset->fY += kMaxPathHeight;
@@ -290,7 +290,7 @@
         }
 
         const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE };
-        const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
+        const SkPathDirection dirs[2] = { SkPathDirection::kCW, SkPathDirection::kCCW };
         const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f };
         const SkPaint::Join joins[3] = { SkPaint::kRound_Join,
                                          SkPaint::kBevel_Join,
@@ -374,7 +374,7 @@
             // inset rings into outsets when adjacent bisector angles converged outside the previous
             // ring due to accumulated error.
             SkPath p3;
-            p3.setFillType(SkPath::kEvenOdd_FillType);
+            p3.setFillType(SkPathFillType::kEvenOdd);
             p3.moveTo(1184.96f, 982.557f);
             p3.lineTo(1183.71f, 982.865f);
             p3.lineTo(1180.99f, 982.734f);
diff --git a/gm/convexpaths.cpp b/gm/convexpaths.cpp
index a92508f..0fa0e34 100644
--- a/gm/convexpaths.cpp
+++ b/gm/convexpaths.cpp
@@ -66,36 +66,36 @@
 
         fPaths.push_back().addRect(0, 0,
                                    100 * SK_Scalar1, 100 * SK_Scalar1,
-                                   SkPath::kCW_Direction);
+                                   SkPathDirection::kCW);
 
         fPaths.push_back().addRect(0, 0,
                                    100 * SK_Scalar1, 100 * SK_Scalar1,
-                                   SkPath::kCCW_Direction);
+                                   SkPathDirection::kCCW);
 
         fPaths.push_back().addCircle(50  * SK_Scalar1, 50  * SK_Scalar1,
-                                     50  * SK_Scalar1, SkPath::kCW_Direction);
+                                     50  * SK_Scalar1, SkPathDirection::kCW);
 
 
         fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
                                                     50 * SK_Scalar1,
                                                     100 * SK_Scalar1),
-                                   SkPath::kCW_Direction);
+                                   SkPathDirection::kCW);
 
         fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
                                                     100 * SK_Scalar1,
                                                     5 * SK_Scalar1),
-                                   SkPath::kCCW_Direction);
+                                   SkPathDirection::kCCW);
 
         fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
                                                     SK_Scalar1,
                                                     100 * SK_Scalar1),
-                                                    SkPath::kCCW_Direction);
+                                                    SkPathDirection::kCCW);
 
         fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
                                                          SK_Scalar1 * 100,
                                                          SK_Scalar1 * 100),
                                         40 * SK_Scalar1, 20 * SK_Scalar1,
-                                        SkPath::kCW_Direction);
+                                        SkPathDirection::kCW);
 
         // large number of points
         enum {
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 67a84b3..68d3289 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -15,128 +15,26 @@
 #include "include/core/SkPath.h"
 #include "include/core/SkPoint.h"
 #include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
 #include "include/core/SkScalar.h"
 #include "include/core/SkSize.h"
 #include "include/core/SkString.h"
-#include "include/core/SkTypes.h"
 #include "include/gpu/GrContext.h"
-#include "include/private/GrRecordingContext.h"
 #include "include/private/GrSharedEnums.h"
 #include "include/private/GrTypesPriv.h"
-#include "include/private/SkColorData.h"
-#include "src/core/SkPointPriv.h"
 #include "src/core/SkTLList.h"
-#include "src/gpu/GrCaps.h"
-#include "src/gpu/GrDefaultGeoProcFactory.h"
 #include "src/gpu/GrFragmentProcessor.h"
-#include "src/gpu/GrGeometryProcessor.h"
-#include "src/gpu/GrMemoryPool.h"
-#include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrPaint.h"
-#include "src/gpu/GrProcessorAnalysis.h"
-#include "src/gpu/GrProcessorSet.h"
-#include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
-#include "src/gpu/GrUserStencilSettings.h"
 #include "src/gpu/effects/GrConvexPolyEffect.h"
-#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
-#include "src/gpu/ops/GrDrawOp.h"
-#include "src/gpu/ops/GrMeshDrawOp.h"
-#include "src/gpu/ops/GrOp.h"
+#include "tools/gpu/TestOps.h"
 
 #include <memory>
 #include <utility>
 
 class GrAppliedClip;
 
-/** outset rendered rect to visualize anti-aliased poly edges */
-static SkRect outset(const SkRect& unsorted) {
-    SkRect r = unsorted;
-    r.outset(5.f, 5.f);
-    return r;
-}
-
-/** sorts a rect */
-static SkRect sorted_rect(const SkRect& unsorted) {
-    SkRect r = unsorted;
-    r.sort();
-    return r;
-}
-
 namespace skiagm {
-class PolyBoundsOp : public GrMeshDrawOp {
-public:
-    DEFINE_OP_CLASS_ID
-
-    static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                                          GrPaint&& paint,
-                                          const SkRect& rect) {
-        GrOpMemoryPool* pool = context->priv().opMemoryPool();
-
-        return pool->allocate<PolyBoundsOp>(std::move(paint), rect);
-    }
-
-    const char* name() const override { return "PolyBoundsOp"; }
-
-    void visitProxies(const VisitProxyFunc& func) const override {
-        fProcessors.visitProxies(func);
-    }
-
-    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
-
-    GrProcessorSet::Analysis finalize(
-            const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
-            GrClampType clampType) override {
-        return fProcessors.finalize(
-                fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
-                hasMixedSampledCoverage, caps, clampType, &fColor);
-    }
-
-private:
-    friend class ::GrOpMemoryPool; // for ctor
-
-    PolyBoundsOp(GrPaint&& paint, const SkRect& rect)
-            : INHERITED(ClassID())
-            , fColor(paint.getColor4f())
-            , fProcessors(std::move(paint))
-            , fRect(outset(rect)) {
-        this->setBounds(sorted_rect(fRect), HasAABloat::kNo, IsHairline::kNo);
-    }
-
-    void onPrepareDraws(Target* target) override {
-        using namespace GrDefaultGeoProcFactory;
-
-        Color color(fColor);
-        sk_sp<GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Make(
-                target->caps().shaderCaps(),
-                color,
-                Coverage::kSolid_Type,
-                LocalCoords::kUnused_Type,
-                SkMatrix::I()));
-
-        SkASSERT(gp->vertexStride() == sizeof(SkPoint));
-        QuadHelper helper(target, sizeof(SkPoint), 1);
-        SkPoint* verts = reinterpret_cast<SkPoint*>(helper.vertices());
-        if (!verts) {
-            return;
-        }
-
-        SkPointPriv::SetRectTriStrip(verts, fRect, sizeof(SkPoint));
-        helper.recordDraw(target, std::move(gp));
-    }
-
-    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
-        flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, std::move(fProcessors));
-    }
-
-    SkPMColor4f fColor;
-    GrProcessorSet fProcessors;
-    SkRect fRect;
-
-    typedef GrMeshDrawOp INHERITED;
-};
 
 /**
  * This GM directly exercises a GrProcessor that draws convex polygons.
@@ -213,7 +111,9 @@
     void onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                 SkCanvas* canvas) override {
         SkScalar y = 0;
-        constexpr SkScalar kDX = 12.f;
+        static constexpr SkScalar kDX = 12.f;
+        static constexpr SkScalar kOutset = 5.f;
+
         for (PathList::Iter iter(fPaths, PathList::Iter::kHead_IterStart);
              iter.get();
              iter.next()) {
@@ -236,8 +136,8 @@
                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                 grPaint.addCoverageFragmentProcessor(std::move(fp));
 
-                std::unique_ptr<GrDrawOp> op =
-                        PolyBoundsOp::Make(context, std::move(grPaint), p.getBounds());
+                auto rect = p.getBounds().makeOutset(kOutset, kOutset);
+                auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(grPaint), rect);
                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
 
                 x += SkScalarCeilToScalar(path->getBounds().width() + kDX);
@@ -263,8 +163,7 @@
             SkScalar x = 0;
 
             for (int et = 0; et < kGrClipEdgeTypeCnt; ++et) {
-                SkRect rect = *iter.get();
-                rect.offset(x, y);
+                SkRect rect = iter.get()->makeOffset(x, y);
                 GrClipEdgeType edgeType = (GrClipEdgeType) et;
                 std::unique_ptr<GrFragmentProcessor> fp(GrConvexPolyEffect::Make(edgeType, rect));
                 if (!fp) {
@@ -276,8 +175,9 @@
                 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
                 grPaint.addCoverageFragmentProcessor(std::move(fp));
 
-                std::unique_ptr<GrDrawOp> op = PolyBoundsOp::Make(context, std::move(grPaint),
-                                                                  rect);
+                auto drawRect = rect.makeOutset(kOutset, kOutset);
+                auto op = sk_gpu_test::test_ops::MakeRect(context, std::move(grPaint), drawRect);
+
                 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
 
                 x += SkScalarCeilToScalar(rect.width() + kDX);
diff --git a/gm/crbug_788500.cpp b/gm/crbug_788500.cpp
index 9a35bfc..a0a1ad3 100644
--- a/gm/crbug_788500.cpp
+++ b/gm/crbug_788500.cpp
@@ -12,7 +12,7 @@
 
 DEF_SIMPLE_GM(crbug_788500, canvas, 300, 300) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.moveTo(245.5f, 98.5f);
     path.cubicTo(245.5f, 98.5f, 242, 78, 260, 75);
diff --git a/gm/crbug_908646.cpp b/gm/crbug_908646.cpp
index 843799e..3d11276 100644
--- a/gm/crbug_908646.cpp
+++ b/gm/crbug_908646.cpp
@@ -14,7 +14,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(50,  50);
     path.lineTo(50,  300);
     path.lineTo(250, 300);
diff --git a/gm/cubicpaths.cpp b/gm/cubicpaths.cpp
index b38c0f8..a09d612 100644
--- a/gm/cubicpaths.cpp
+++ b/gm/cubicpaths.cpp
@@ -125,7 +125,7 @@
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
+                  SkPaint::Style style, SkPathFillType fill,
                   SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -142,14 +142,14 @@
 
     void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
@@ -244,7 +244,7 @@
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
+                  SkPaint::Style style, SkPathFillType fill,
                   SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -261,14 +261,14 @@
 
     virtual void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
index e733528..f96d049 100644
--- a/gm/degeneratesegments.cpp
+++ b/gm/degeneratesegments.cpp
@@ -197,7 +197,7 @@
 
     void drawPath(SkPath& path, SkCanvas* canvas, SkColor color,
                   const SkRect& clip, SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
+                  SkPaint::Style style, SkPathFillType fill,
                   SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -261,14 +261,14 @@
         };
 
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"}
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
diff --git a/gm/drawatlas.cpp b/gm/drawatlas.cpp
index 132fd5b..e349bbf 100644
--- a/gm/drawatlas.cpp
+++ b/gm/drawatlas.cpp
@@ -208,8 +208,8 @@
     SkPath path;
     const float baseline_offset = -5;
 
-    const SkPath::Direction dirs[] = {
-        SkPath::kCW_Direction, SkPath::kCCW_Direction,
+    const SkPathDirection dirs[] = {
+        SkPathDirection::kCW, SkPathDirection::kCCW,
     };
     for (auto d : dirs) {
         path.reset();
diff --git a/gm/dstreadshuffle.cpp b/gm/dstreadshuffle.cpp
index d9a2a9f..9e6a7ad 100644
--- a/gm/dstreadshuffle.cpp
+++ b/gm/dstreadshuffle.cpp
@@ -90,7 +90,7 @@
                     for (int i = 0; i < 5; ++i) {
                         fConcavePath.lineTo(points[(2 * i) % 5]);
                     }
-                    fConcavePath.setFillType(SkPath::kEvenOdd_FillType);
+                    fConcavePath.setFillType(SkPathFillType::kEvenOdd);
                     SkASSERT(!fConcavePath.isConvex());
                 }
                 canvas->drawPath(fConcavePath, *paint);
diff --git a/gm/ducky_yuv_blend.cpp b/gm/ducky_yuv_blend.cpp
new file mode 100644
index 0000000..75839a6
--- /dev/null
+++ b/gm/ducky_yuv_blend.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "gm/gm.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkYUVAIndex.h"
+#include "include/core/SkYUVASizeInfo.h"
+#include "src/core/SkCachedData.h"
+#include "src/image/SkImage_Base.h"
+#include "tools/Resources.h"
+#include "tools/ToolUtils.h"
+
+// Modeled on the layout test css3/blending/background-blend-mode-image-image.html to reproduce
+// skbug.com/9619
+DEF_SIMPLE_GM_CAN_FAIL(ducky_yuv_blend, canvas, errorMsg, 560, 1130) {
+    sk_sp<SkImage> duckyBG = GetResourceAsImage("ducky.png");
+    sk_sp<SkImage> duckyFG[2] = {GetResourceAsImage("ducky.jpg"), nullptr};
+    if (!duckyFG[0] || !duckyBG) {
+        *errorMsg = "Image(s) failed to load.";
+        return skiagm::DrawResult::kFail;
+    }
+
+    // If we're on the GPU we do a second round of draws where the source image is YUV planes.
+    // Otherwise we just draw the original again,
+    if (auto* context = canvas->getGrContext()) {
+        SkYUVASizeInfo info;
+        SkYUVAIndex indices[4];
+        SkYUVColorSpace yuvColorSpace;
+        const void* planes[4];
+        auto data = as_IB(duckyFG[0])->getPlanes(&info, indices, &yuvColorSpace, planes);
+        SkPixmap pixmaps[4];
+        for (int i = 0; i < 4; ++i) {
+            if (indices[i].fIndex >= 0) {
+                pixmaps[i].reset(
+                        SkImageInfo::MakeA8(info.fSizes[i]), planes[i], info.fWidthBytes[i]);
+            }
+        }
+        duckyFG[1] = SkImage::MakeFromYUVAPixmaps(context,
+                                                  yuvColorSpace,
+                                                  pixmaps,
+                                                  indices,
+                                                  duckyFG[0]->dimensions(),
+                                                  kTopLeft_GrSurfaceOrigin,
+                                                  true);
+    } else {
+        duckyFG[1] = duckyFG[0];
+    }
+
+    static constexpr int kNumPerRow = 4;
+    static constexpr int kPad = 10;
+    static constexpr auto kDstRect = SkRect::MakeWH(130, 130);
+    int rowCnt = 0;
+    canvas->translate(kPad, kPad);
+    canvas->save();
+    auto newRow = [&] {
+        canvas->restore();
+        canvas->translate(0, kDstRect.height() + kPad);
+        canvas->save();
+        rowCnt = 0;
+    };
+    ToolUtils::draw_checkerboard(
+            canvas, SK_ColorDKGRAY, SK_ColorLTGRAY, (kDstRect.height() + kPad)/5);
+    for (auto& fg : duckyFG) {
+        for (int bm = static_cast<int>(SkBlendMode::kLastCoeffMode) + 1;
+             bm < static_cast<int>(SkBlendMode::kLastMode);
+             ++bm) {
+            auto mode = static_cast<SkBlendMode>(bm);
+            SkPaint paint;
+            paint.setFilterQuality(kMedium_SkFilterQuality);
+            canvas->drawImageRect(duckyBG, kDstRect, &paint);
+            paint.setBlendMode(mode);
+            canvas->drawImageRect(fg, kDstRect, &paint);
+            canvas->translate(kDstRect.width() + kPad, 0);
+            if (++rowCnt == kNumPerRow) {
+                newRow();
+            }
+        }
+        // Force a new row between the two foreground images
+        newRow();
+    }
+    canvas->restore();
+    return skiagm::DrawResult::kOk;
+}
diff --git a/gm/emboss.cpp b/gm/emboss.cpp
index bb98546..82a13b3 100644
--- a/gm/emboss.cpp
+++ b/gm/emboss.cpp
@@ -81,6 +81,10 @@
 
         paint.setStyle(SkPaint::kFill_Style);
         canvas->drawString("Hello", 0, 50, SkFont(nullptr, 50), paint);
+
+        paint.setShader(nullptr);
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawString("World", 0, 100, SkFont(nullptr, 50), paint);
     }
 
 private:
diff --git a/gm/emptypath.cpp b/gm/emptypath.cpp
index 126db9e..154ece5 100644
--- a/gm/emptypath.cpp
+++ b/gm/emptypath.cpp
@@ -32,7 +32,7 @@
                     SkColor color,
                     const SkRect& clip,
                     SkPaint::Style style,
-                    SkPath::FillType fill) {
+                    SkPathFillType fill) {
         SkPath path;
         path.setFillType(fill);
         SkPaint paint;
@@ -46,14 +46,14 @@
 
     void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
diff --git a/gm/filltypes.cpp b/gm/filltypes.cpp
index 0b251eb..f8d74da 100644
--- a/gm/filltypes.cpp
+++ b/gm/filltypes.cpp
@@ -42,7 +42,7 @@
         return SkISize::Make(835, 840);
     }
 
-    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+    void showPath(SkCanvas* canvas, int x, int y, SkPathFillType ft,
                   SkScalar scale, const SkPaint& paint) {
         const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
 
@@ -59,13 +59,13 @@
     }
 
     void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
-        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+        showPath(canvas,   0,   0, SkPathFillType::kWinding,
                  scale, paint);
-        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+        showPath(canvas, 200,   0, SkPathFillType::kEvenOdd,
                  scale, paint);
-        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+        showPath(canvas,  00, 200, SkPathFillType::kInverseWinding,
                  scale, paint);
-        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+        showPath(canvas, 200, 200, SkPathFillType::kInverseEvenOdd,
                  scale, paint);
     }
 
diff --git a/gm/filltypespersp.cpp b/gm/filltypespersp.cpp
index 4edaa3a..b3d9caa 100644
--- a/gm/filltypespersp.cpp
+++ b/gm/filltypespersp.cpp
@@ -46,7 +46,7 @@
         return SkISize::Make(835, 840);
     }
 
-    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+    void showPath(SkCanvas* canvas, int x, int y, SkPathFillType ft,
                   SkScalar scale, const SkPaint& paint) {
         const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
 
@@ -75,13 +75,13 @@
                                                      SkTileMode::kClamp));
         paint.setAntiAlias(aa);
 
-        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+        showPath(canvas,   0,   0, SkPathFillType::kWinding,
                  scale, paint);
-        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+        showPath(canvas, 200,   0, SkPathFillType::kEvenOdd,
                  scale, paint);
-        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+        showPath(canvas,  00, 200, SkPathFillType::kInverseWinding,
                  scale, paint);
-        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+        showPath(canvas, 200, 200, SkPathFillType::kInverseEvenOdd,
                  scale, paint);
     }
 
diff --git a/gm/fontregen.cpp b/gm/fontregen.cpp
index 3addd80..8714815 100644
--- a/gm/fontregen.cpp
+++ b/gm/fontregen.cpp
@@ -17,6 +17,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkColor.h"
 #include "include/core/SkFont.h"
+#include "include/core/SkFontMgr.h"
 #include "include/core/SkFontStyle.h"
 #include "include/core/SkFontTypes.h"
 #include "include/core/SkPaint.h"
@@ -109,3 +110,48 @@
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_GM(return new FontRegenGM())
+
+///////////////////////////////////////////////////////////////////////////////
+
+class BadAppleGM : public skiagm::GpuGM {
+
+    SkString onShortName() override { return SkString("badapple"); }
+
+    SkISize onISize() override { return {kSize, kSize}; }
+
+    void onOnceBeforeDraw() override {
+        this->setBGColor(SK_ColorWHITE);
+        auto fm = SkFontMgr::RefDefault();
+
+        static const SkString kTexts[] = {
+                SkString("Meet"),
+                SkString("iPad Pro"),
+        };
+
+        SkFont font;
+        font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
+        font.setSubpixel(true);
+        font.setSize(256);
+
+        fBlobs[0] = make_blob(kTexts[0], font);
+        fBlobs[1] = make_blob(kTexts[1], font);
+    }
+
+    void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
+        SkPaint paint;
+        paint.setColor(0xFF111111);
+        canvas->drawTextBlob(fBlobs[0], 10, 260, paint);
+        canvas->drawTextBlob(fBlobs[1], 10, 500, paint);
+        context->flush();
+    }
+
+private:
+    static constexpr int kSize = 512;
+
+    sk_sp<SkTextBlob> fBlobs[3];
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new BadAppleGM())
diff --git a/gm/fpcoordinateoverride.cpp b/gm/fpcoordinateoverride.cpp
index 212fd25..3877f40 100644
--- a/gm/fpcoordinateoverride.cpp
+++ b/gm/fpcoordinateoverride.cpp
@@ -26,6 +26,7 @@
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/effects/GrRRectEffect.h"
 #include "src/gpu/effects/GrSkSLFP.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/ops/GrFillRectOp.h"
 #include "tools/Resources.h"
 #include "tools/ToolUtils.h"
@@ -36,7 +37,7 @@
 
     SampleCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
         : INHERITED(CLASS_ID, kNone_OptimizationFlags) {
-        child->setComputeLocalCoordsInVertexShader(false);
+        child->setSampledWithExplicitCoords(true);
         this->registerChildProcessor(std::move(child));
     }
 
@@ -83,10 +84,9 @@
     SkBitmap bmp;
     GetResourceAsBitmap("images/mandrill_512_q075.jpg", &bmp);
     GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
-    GrColorType srcColorType = SkColorTypeToGrColorType(bmp.colorType());
     sk_sp<GrTextureProxy> texture = proxyProvider->createProxyFromBitmap(bmp, GrMipMapped::kNo);
-    std::unique_ptr<GrFragmentProcessor> imgFP = GrSimpleTextureEffect::Make(texture, srcColorType,
-                                                                             SkMatrix());
+    std::unique_ptr<GrFragmentProcessor> imgFP =
+            GrSimpleTextureEffect::Make(texture, bmp.alphaType(), SkMatrix());
     auto fp = std::unique_ptr<GrFragmentProcessor>(new SampleCoordEffect(std::move(imgFP)));
 
     GrPaint grPaint;
diff --git a/gm/fwidth_squircle.cpp b/gm/fwidth_squircle.cpp
index cc51cce..3eae01f 100644
--- a/gm/fwidth_squircle.cpp
+++ b/gm/fwidth_squircle.cpp
@@ -46,6 +46,7 @@
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 #include "src/gpu/ops/GrDrawOp.h"
 #include "src/gpu/ops/GrOp.h"
+#include "tools/gpu/ProxyUtils.h"
 
 #include <memory>
 #include <utility>
@@ -65,19 +66,30 @@
 
 class FwidthSquircleTestProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix) {
+        return arena->make<FwidthSquircleTestProcessor>(viewMatrix);
+    }
+
+    const char* name() const override { return "FwidthSquircleTestProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     FwidthSquircleTestProcessor(const SkMatrix& viewMatrix)
             : GrGeometryProcessor(kFwidthSquircleTestProcessor_ClassID)
             , fViewMatrix(viewMatrix) {
         this->setVertexAttributes(&gVertex, 1);
     }
-    const char* name() const override { return "FwidthSquircleTestProcessor"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
 
-private:
     const SkMatrix fViewMatrix;
 
     class Impl;
+
+    typedef GrGeometryProcessor INHERITED;
 };
 
 class FwidthSquircleTestProcessor::Impl : public GrGLSLGeometryProcessor {
@@ -119,7 +131,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange&) override {
         const auto& proc = primProc.cast<FwidthSquircleTestProcessor>();
         pdman.setSkMatrix(fViewMatrixHandle, proc.fViewMatrix);
     }
@@ -153,11 +165,30 @@
 
     const char* name() const override { return "FwidthSquircleTestOp"; }
     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
-    GrProcessorSet::Analysis finalize(
-            const GrCaps&, const GrAppliedClip*, bool hasMixedSampledCoverage, GrClampType) override {
+    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
+                                      bool hasMixedSampledCoverage, GrClampType) override {
         return GrProcessorSet::EmptySetAnalysis();
     }
-    void onPrepare(GrOpFlushState* flushState) override {
+
+    void onPrePrepare(GrRecordingContext* context,
+                      const GrSurfaceProxyView* dstView,
+                      GrAppliedClip* clip,
+                      const GrXferProcessor::DstProxyView& dstProxyView) final {
+        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
+
+        // This is equivalent to a GrOpFlushState::detachAppliedClip
+        GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
+
+        GrGeometryProcessor* geomProc = FwidthSquircleTestProcessor::Make(arena, fViewMatrix);
+
+        // TODO: need to also give this to the recording context
+        fProgramInfo = sk_gpu_test::CreateProgramInfo(context->priv().caps(), arena, dstView,
+                                                      std::move(appliedClip), dstProxyView,
+                                                      geomProc, SkBlendMode::kSrcOver,
+                                                      GrPrimitiveType::kTriangleStrip);
+    }
+
+    void onPrepare(GrOpFlushState* flushState) final {
         SkPoint vertices[4] = {
             {-1, -1},
             {+1, -1},
@@ -167,33 +198,50 @@
         fVertexBuffer = flushState->resourceProvider()->createBuffer(
                 sizeof(vertices), GrGpuBufferType::kVertex, kStatic_GrAccessPattern, vertices);
     }
-    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
         if (!fVertexBuffer) {
             return;
         }
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
-                            flushState->drawOpArgs().outputSwizzle());
 
-        FwidthSquircleTestProcessor primProc(fViewMatrix);
+        if (!fProgramInfo) {
+            auto geomProc = FwidthSquircleTestProcessor::Make(flushState->allocator(),
+                                                              fViewMatrix);
 
-        GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                                  flushState->drawOpArgs().origin(),
-                                  pipeline,
-                                  primProc,
-                                  nullptr, nullptr, 0);
+            fProgramInfo = sk_gpu_test::CreateProgramInfo(&flushState->caps(),
+                                                          flushState->allocator(),
+                                                          flushState->view(),
+                                                          flushState->detachAppliedClip(),
+                                                          flushState->dstProxyView(),
+                                                          geomProc, SkBlendMode::kSrcOver,
+                                                          GrPrimitiveType::kTriangleStrip);
+        }
 
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
         mesh.setNonIndexedNonInstanced(4);
         mesh.setVertexData(std::move(fVertexBuffer));
-        flushState->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::MakeIWH(kWidth, kHeight));
+
+        flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1,
+                                          SkRect::MakeIWH(kWidth, kHeight));
+
     }
 
-    sk_sp<GrBuffer> fVertexBuffer;
-    const SkMatrix fViewMatrix;
     static const int kWidth = 200;
     static const int kHeight = 200;
 
+    sk_sp<GrBuffer> fVertexBuffer;
+    const SkMatrix  fViewMatrix;
+
+    // The program info (and both the GrPipeline and GrPrimitiveProcessor it relies on), when
+    // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
+    // arena's job to free up their memory so we just have a bare programInfo pointer here. We
+    // don't even store the GrPipeline and GrPrimitiveProcessor pointers here bc they are
+    // guaranteed to have the same lifetime as the program info.
+    GrProgramInfo*  fProgramInfo = nullptr;
+
     friend class ::GrOpMemoryPool; // for ctor
+
+    typedef GrDrawOp INHERITED;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/gm/hittestpath.cpp b/gm/hittestpath.cpp
index 27dab17..d482e49 100644
--- a/gm/hittestpath.cpp
+++ b/gm/hittestpath.cpp
@@ -53,13 +53,13 @@
                          randoms[10] * scale, randoms[11] * scale);
         }
 
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
         path.offset(SkIntToScalar(20), SkIntToScalar(20));
 
         test_hittest(canvas, path);
 
         canvas->translate(SkIntToScalar(scale), 0);
-        path.setFillType(SkPath::kWinding_FillType);
+        path.setFillType(SkPathFillType::kWinding);
 
         test_hittest(canvas, path);
 }
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 08895f3..023e369 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -197,8 +197,8 @@
 
         GrMipMapped mipMapped = willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo;
 
-        return GrSurfaceProxy::Copy(fCtx.get(), fProxy.get(),
-                SkColorTypeToGrColorType(info.colorType()), mipMapped,
+        return GrSurfaceProxy::Copy(
+                fCtx.get(), fProxy.get(), mipMapped,
                 SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
                 SkBackingFit::kExact, SkBudgeted::kYes);
     }
diff --git a/gm/imagefromyuvtextures.cpp b/gm/imagefromyuvtextures.cpp
index 68dc7dc..d4b96ec 100644
--- a/gm/imagefromyuvtextures.cpp
+++ b/gm/imagefromyuvtextures.cpp
@@ -58,7 +58,9 @@
     }
 
     SkISize onISize() override {
-        return SkISize::Make(kBmpSize + 2 * kPad, 390);
+        // Original image, plus each color space drawn twice
+        int numBitmaps = 2 * (kLastEnum_SkYUVColorSpace + 1) + 1;
+        return SkISize::Make(kBmpSize + 2 * kPad, numBitmaps * (kBmpSize + kPad) + kPad);
     }
 
     void onOnceBeforeDraw() override {
diff --git a/gm/inverseclip.cpp b/gm/inverseclip.cpp
index f7dfb79..9d7b910 100644
--- a/gm/inverseclip.cpp
+++ b/gm/inverseclip.cpp
@@ -12,7 +12,7 @@
 // Repro case for http://skbug.com/9453
 DEF_SIMPLE_GM(inverseclip, canvas, 400, 400) {
     SkPath clip;
-    clip.setFillType(SkPath::kInverseWinding_FillType);
+    clip.setFillType(SkPathFillType::kInverseWinding);
     clip.moveTo(195.448f, 31);
     clip.cubicTo(97.9925f, 31, 18.99f, 105.23f, 18.99f, 196.797f);
     clip.cubicTo(18.99f, 288.365f, 97.9925f, 362.595f, 195.448f, 362.595f);
diff --git a/gm/inversepaths.cpp b/gm/inversepaths.cpp
index 0726983..a6cd1bd 100644
--- a/gm/inversepaths.cpp
+++ b/gm/inversepaths.cpp
@@ -34,7 +34,7 @@
 
 static SkPath generate_circle(SkScalar cx, SkScalar cy, SkScalar d) {
     SkPath path;
-    path.addCircle(cx, cy, d/2, SkPath::kCW_Direction);
+    path.addCircle(cx, cy, d/2, SkPathDirection::kCW);
     return path;
 }
 
@@ -131,10 +131,10 @@
                     canvas->clipRect(clipRect);
 
                     SkPath path = paths[pathIndex](cx, cy, size);
-                    path.setFillType(SkPath::kInverseWinding_FillType);
+                    path.setFillType(SkPathFillType::kInverseWinding);
                     canvas->drawPath(path, paint);
 
-                    path.setFillType(SkPath::kWinding_FillType);
+                    path.setFillType(SkPathFillType::kWinding);
                     canvas->drawPath(path, outlinePaint);
 
                     canvas->restore();
diff --git a/gm/linepaths.cpp b/gm/linepaths.cpp
index 25ffde2..c4e6ffd 100644
--- a/gm/linepaths.cpp
+++ b/gm/linepaths.cpp
@@ -20,7 +20,7 @@
 
 static void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                      const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                     SkPaint::Style style, SkPath::FillType fill,
+                     SkPaint::Style style, SkPathFillType fill,
                      SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -37,14 +37,14 @@
 
 static void draw(SkCanvas* canvas, bool doClose) {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
diff --git a/gm/mandoline.cpp b/gm/mandoline.cpp
index a675013..6f943fc 100644
--- a/gm/mandoline.cpp
+++ b/gm/mandoline.cpp
@@ -28,7 +28,7 @@
     static constexpr int kDefaultSubdivisions = 10;
 
     MandolineSlicer(SkPoint anchorPt) {
-        fPath.setFillType(SkPath::kEvenOdd_FillType);
+        fPath.setFillType(SkPathFillType::kEvenOdd);
         fPath.setIsVolatile(true);
         this->reset(anchorPt);
     }
diff --git a/gm/multipicturedraw.cpp b/gm/multipicturedraw.cpp
deleted file mode 100644
index 688154e..0000000
--- a/gm/multipicturedraw.cpp
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "gm/gm.h"
-#include "include/core/SkBBHFactory.h"
-#include "include/core/SkBlendMode.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkColor.h"
-#include "include/core/SkColorFilter.h"
-#include "include/core/SkImageInfo.h"
-#include "include/core/SkMatrix.h"
-#include "include/core/SkMultiPictureDraw.h"
-#include "include/core/SkPaint.h"
-#include "include/core/SkPath.h"
-#include "include/core/SkPicture.h"
-#include "include/core/SkPictureRecorder.h"
-#include "include/core/SkRRect.h"
-#include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkScalar.h"
-#include "include/core/SkSize.h"
-#include "include/core/SkString.h"
-#include "include/core/SkSurface.h"
-#include "include/core/SkTypes.h"
-#include "include/private/SkTArray.h"
-#include "tools/ToolUtils.h"
-
-constexpr SkScalar kRoot3Over2 = 0.86602545f;  // sin(60)
-constexpr SkScalar kRoot3      = 1.73205081f;
-
-constexpr int kHexSide = 30;
-constexpr int kNumHexX = 6;
-constexpr int kNumHexY = 6;
-constexpr int kPicWidth = kNumHexX * kHexSide;
-constexpr int kPicHeight = (int)((kNumHexY - 0.5f) * 2 * kHexSide * kRoot3Over2 + 0.5f);
-constexpr SkScalar kInset = 20.0f;
-constexpr int kNumPictures = 4;
-
-constexpr int kTriSide = 40;
-
-// Create a hexagon centered at (originX, originY)
-static SkPath make_hex_path(SkScalar originX, SkScalar originY) {
-    SkPath hex;
-    hex.moveTo(originX-kHexSide, originY);
-    hex.rLineTo(SkScalarHalf(kHexSide), kRoot3Over2 * kHexSide);
-    hex.rLineTo(SkIntToScalar(kHexSide), 0);
-    hex.rLineTo(SkScalarHalf(kHexSide), -kHexSide * kRoot3Over2);
-    hex.rLineTo(-SkScalarHalf(kHexSide), -kHexSide * kRoot3Over2);
-    hex.rLineTo(-SkIntToScalar(kHexSide), 0);
-    hex.close();
-    return hex;
-}
-
-// Make a picture that is a tiling of the plane with stroked hexagons where
-// each hexagon is in its own layer. The layers are to exercise Ganesh's
-// layer hoisting.
-static sk_sp<SkPicture> make_hex_plane_picture(SkColor fillColor) {
-
-    // Create a hexagon with its center at the origin
-    SkPath hex = make_hex_path(0, 0);
-
-    SkPaint fill;
-    fill.setStyle(SkPaint::kFill_Style);
-    fill.setColor(fillColor);
-
-    SkPaint stroke;
-    stroke.setStyle(SkPaint::kStroke_Style);
-    stroke.setStrokeWidth(3);
-
-    SkPictureRecorder recorder;
-    SkRTreeFactory bbhFactory;
-
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
-                                               SkIntToScalar(kPicHeight),
-                                               &bbhFactory);
-
-    SkScalar xPos, yPos = 0;
-
-    for (int y = 0; y < kNumHexY; ++y) {
-        xPos = 0;
-
-        for (int x = 0; x < kNumHexX; ++x) {
-            canvas->saveLayer(nullptr, nullptr);
-            canvas->translate(xPos, yPos + ((x % 2) ? kRoot3Over2 * kHexSide : 0));
-            canvas->drawPath(hex, fill);
-            canvas->drawPath(hex, stroke);
-            canvas->restore();
-
-            xPos += 1.5f * kHexSide;
-        }
-
-        yPos += 2 * kHexSide * kRoot3Over2;
-    }
-
-    return recorder.finishRecordingAsPicture();
-}
-
-// Create a picture that consists of a single large layer that is tiled
-// with hexagons.
-// This is intended to exercise the layer hoisting code's clip handling (in
-// tile mode).
-static sk_sp<SkPicture> make_single_layer_hex_plane_picture() {
-
-    // Create a hexagon with its center at the origin
-    SkPath hex = make_hex_path(0, 0);
-
-    SkPaint whiteFill;
-    whiteFill.setStyle(SkPaint::kFill_Style);
-    whiteFill.setColor(SK_ColorWHITE);
-
-    SkPaint greyFill;
-    greyFill.setStyle(SkPaint::kFill_Style);
-    greyFill.setColor(SK_ColorLTGRAY);
-
-    SkPaint stroke;
-    stroke.setStyle(SkPaint::kStroke_Style);
-    stroke.setStrokeWidth(3);
-
-    SkPictureRecorder recorder;
-    SkRTreeFactory bbhFactory;
-
-    constexpr SkScalar kBig = 10000.0f;
-    SkCanvas* canvas = recorder.beginRecording(kBig, kBig, &bbhFactory);
-
-    canvas->saveLayer(nullptr, nullptr);
-
-    SkScalar xPos = 0.0f, yPos = 0.0f;
-
-    for (int y = 0; yPos < kBig; ++y) {
-        xPos = 0;
-
-        for (int x = 0; xPos < kBig; ++x) {
-            canvas->save();
-            canvas->translate(xPos, yPos + ((x % 2) ? kRoot3Over2 * kHexSide : 0));
-            // The color of the filled hex is swapped to yield a different
-            // pattern in each tile. This allows an error in layer hoisting (e.g.,
-            // the clip isn't blocking cache reuse) to cause a visual discrepancy.
-            canvas->drawPath(hex, ((x+y) % 3) ? whiteFill : greyFill);
-            canvas->drawPath(hex, stroke);
-            canvas->restore();
-
-            xPos += 1.5f * kHexSide;
-        }
-
-        yPos += 2 * kHexSide * kRoot3Over2;
-    }
-
-    canvas->restore();
-
-    return recorder.finishRecordingAsPicture();
-}
-
-// Make an equilateral triangle path with its top corner at (originX, originY)
-static SkPath make_tri_path(SkScalar originX, SkScalar originY) {
-    SkPath tri;
-    tri.moveTo(originX, originY);
-    tri.rLineTo(SkScalarHalf(kTriSide), 1.5f * kTriSide / kRoot3);
-    tri.rLineTo(-kTriSide, 0);
-    tri.close();
-    return tri;
-}
-
-static sk_sp<SkPicture> make_tri_picture() {
-    SkPath tri = make_tri_path(SkScalarHalf(kTriSide), 0);
-
-    SkPaint fill;
-    fill.setStyle(SkPaint::kFill_Style);
-    fill.setColor(SK_ColorLTGRAY);
-
-    SkPaint stroke;
-    stroke.setStyle(SkPaint::kStroke_Style);
-    stroke.setStrokeWidth(3);
-
-    SkPictureRecorder recorder;
-    SkRTreeFactory bbhFactory;
-
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
-                                               SkIntToScalar(kPicHeight),
-                                               &bbhFactory);
-    SkRect r = tri.getBounds();
-    r.outset(2.0f, 2.0f);       // outset for stroke
-    canvas->clipRect(r);
-    // The saveLayer/restore block is to exercise layer hoisting
-    canvas->saveLayer(nullptr, nullptr);
-        canvas->drawPath(tri, fill);
-        canvas->drawPath(tri, stroke);
-    canvas->restore();
-
-    return recorder.finishRecordingAsPicture();
-}
-
-static sk_sp<SkPicture> make_sub_picture(const SkPicture* tri) {
-    SkPictureRecorder recorder;
-    SkRTreeFactory bbhFactory;
-
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
-                                               SkIntToScalar(kPicHeight),
-                                               &bbhFactory);
-
-    canvas->scale(1.0f/2.0f, 1.0f/2.0f);
-
-    canvas->save();
-    canvas->translate(SkScalarHalf(kTriSide), 0);
-    canvas->drawPicture(tri);
-    canvas->restore();
-
-    canvas->save();
-    canvas->translate(SkIntToScalar(kTriSide), 1.5f * kTriSide / kRoot3);
-    canvas->drawPicture(tri);
-    canvas->restore();
-
-    canvas->save();
-    canvas->translate(0, 1.5f * kTriSide / kRoot3);
-    canvas->drawPicture(tri);
-    canvas->restore();
-
-    return recorder.finishRecordingAsPicture();
-}
-
-// Create a Sierpinkski-like picture that starts with a top row with a picture
-// that just contains a triangle. Subsequent rows take the prior row's picture,
-// shrinks it and replicates it 3 times then draws and appropriate number of
-// copies of it.
-static sk_sp<SkPicture> make_sierpinski_picture() {
-    sk_sp<SkPicture> pic(make_tri_picture());
-
-    SkPictureRecorder recorder;
-    SkRTreeFactory bbhFactory;
-
-    SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
-                                               SkIntToScalar(kPicHeight),
-                                               &bbhFactory);
-
-    constexpr int kNumLevels = 4;
-    for (int i = 0; i < kNumLevels; ++i) {
-        canvas->save();
-            canvas->translate(kPicWidth/2 - (i+1) * (kTriSide/2.0f), 0.0f);
-            for (int j = 0; j < i+1; ++j) {
-                canvas->drawPicture(pic);
-                canvas->translate(SkIntToScalar(kTriSide), 0);
-            }
-        canvas->restore();
-
-        pic = make_sub_picture(pic.get());
-
-        canvas->translate(0, 1.5f * kTriSide / kRoot3);
-    }
-
-    return recorder.finishRecordingAsPicture();
-}
-
-static sk_sp<SkSurface> create_compat_surface(SkCanvas* canvas, int width, int height) {
-    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
-
-    return ToolUtils::makeSurface(canvas, info);
-}
-
-// This class stores the information required to compose all the result
-// fragments potentially generated by the MultiPictureDraw object
-class ComposeStep {
-public:
-    ComposeStep() : fX(0.0f), fY(0.0f), fPaint(nullptr) { }
-    ~ComposeStep() {
-        delete fPaint;
-    }
-
-    sk_sp<SkSurface> fSurf;
-    SkScalar   fX;
-    SkScalar   fY;
-    SkPaint*   fPaint;
-};
-
-typedef void (*PFContentMtd)(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]);
-
-// Just a single picture with no clip
-static void no_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[0]);
-}
-
-// Two pictures with a rect clip on the second one
-static void rect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[0]);
-
-    SkRect rect = pictures[0]->cullRect();
-    rect.inset(kInset, kInset);
-
-    canvas->clipRect(rect);
-
-    canvas->drawPicture(pictures[1]);
-}
-
-// Two pictures with a round rect clip on the second one
-static void rrect_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[0]);
-
-    SkRect rect = pictures[0]->cullRect();
-    rect.inset(kInset, kInset);
-
-    SkRRect rrect;
-    rrect.setRectXY(rect, kInset, kInset);
-
-    canvas->clipRRect(rrect);
-
-    canvas->drawPicture(pictures[1]);
-}
-
-// Two pictures with a clip path on the second one
-static void path_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[0]);
-
-    // Create a hexagon centered on the middle of the hex grid
-    SkPath hex = make_hex_path((kNumHexX / 2.0f) * kHexSide, kNumHexY * kHexSide * kRoot3Over2);
-
-    canvas->clipPath(hex);
-
-    canvas->drawPicture(pictures[1]);
-}
-
-// Two pictures with an inverse clip path on the second one
-static void invpath_clip(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[0]);
-
-    // Create a hexagon centered on the middle of the hex grid
-    SkPath hex = make_hex_path((kNumHexX / 2.0f) * kHexSide, kNumHexY * kHexSide * kRoot3Over2);
-    hex.setFillType(SkPath::kInverseEvenOdd_FillType);
-
-    canvas->clipPath(hex);
-
-    canvas->drawPicture(pictures[1]);
-}
-
-// Reuse a single base (triangular) picture a _lot_ (rotated, scaled and translated).
-static void sierpinski(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->save();
-        canvas->drawPicture(pictures[2]);
-
-        canvas->rotate(180.0f);
-        canvas->translate(-SkIntToScalar(kPicWidth), -SkIntToScalar(kPicHeight));
-        canvas->drawPicture(pictures[2]);
-    canvas->restore();
-}
-
-static void big_layer(SkCanvas* canvas, const SkPicture* pictures[kNumPictures]) {
-    canvas->drawPicture(pictures[3]);
-}
-
-constexpr PFContentMtd gContentMthds[] = {
-    no_clip,
-    rect_clip,
-    rrect_clip,
-    path_clip,
-    invpath_clip,
-    sierpinski,
-    big_layer,
-};
-
-static void create_content(SkMultiPictureDraw* mpd, PFContentMtd pfGen,
-                           const SkPicture* pictures[kNumPictures],
-                           SkCanvas* dest, const SkMatrix& xform) {
-    sk_sp<SkPicture> composite;
-
-    {
-        SkPictureRecorder recorder;
-        SkRTreeFactory bbhFactory;
-
-        SkCanvas* pictureCanvas = recorder.beginRecording(SkIntToScalar(kPicWidth),
-                                                          SkIntToScalar(kPicHeight),
-                                                          &bbhFactory);
-
-        (*pfGen)(pictureCanvas, pictures);
-
-        composite = recorder.finishRecordingAsPicture();
-    }
-
-    mpd->add(dest, composite.get(), &xform);
-}
-
-typedef void(*PFLayoutMtd)(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
-                           PFContentMtd pfGen, const SkPicture* pictures[kNumPictures],
-                           SkTArray<ComposeStep>* composeSteps);
-
-// Draw the content into a single canvas
-static void simple(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
-                   PFContentMtd pfGen,
-                   const SkPicture* pictures[kNumPictures],
-                   SkTArray<ComposeStep> *composeSteps) {
-
-    ComposeStep& step = composeSteps->push_back();
-
-    step.fSurf = create_compat_surface(finalCanvas, kPicWidth, kPicHeight);
-
-    SkCanvas* subCanvas = step.fSurf->getCanvas();
-
-    create_content(mpd, pfGen, pictures, subCanvas, SkMatrix::I());
-}
-
-// Draw the content into multiple canvases/tiles
-static void tiled(SkCanvas* finalCanvas, SkMultiPictureDraw* mpd,
-                  PFContentMtd pfGen,
-                  const SkPicture* pictures[kNumPictures],
-                  SkTArray<ComposeStep> *composeSteps) {
-    const int kNumTilesX = 2;
-    const int kNumTilesY = 2;
-    const int kTileWidth = kPicWidth / kNumTilesX;
-    const int kTileHeight = kPicHeight / kNumTilesY;
-
-    SkASSERT(kPicWidth == kNumTilesX * kTileWidth);
-    SkASSERT(kPicHeight == kNumTilesY * kTileHeight);
-
-    const SkColor colors[kNumTilesX][kNumTilesY] = {
-        { SK_ColorCYAN,   SK_ColorMAGENTA },
-        { SK_ColorYELLOW, SK_ColorGREEN   }
-    };
-
-    for (int y = 0; y < kNumTilesY; ++y) {
-        for (int x = 0; x < kNumTilesX; ++x) {
-            ComposeStep& step = composeSteps->push_back();
-
-            step.fX = SkIntToScalar(x*kTileWidth);
-            step.fY = SkIntToScalar(y*kTileHeight);
-            step.fPaint = new SkPaint;
-            step.fPaint->setColorFilter(
-                SkColorFilters::Blend(colors[x][y], SkBlendMode::kModulate));
-
-            step.fSurf = create_compat_surface(finalCanvas, kTileWidth, kTileHeight);
-
-            SkCanvas* subCanvas = step.fSurf->getCanvas();
-
-            const SkMatrix trans = SkMatrix::MakeTrans(-SkIntToScalar(x*kTileWidth),
-                                                       -SkIntToScalar(y*kTileHeight));
-
-            create_content(mpd, pfGen, pictures, subCanvas, trans);
-        }
-    }
-}
-
-constexpr PFLayoutMtd gLayoutMthds[] = { simple, tiled };
-
-namespace skiagm {
-    /**
-     * This GM exercises the SkMultiPictureDraw object. It tests the
-     * cross product of:
-     *      tiled vs. all-at-once rendering (e.g., into many or just 1 canvas)
-     *      different clips (e.g., none, rect, rrect)
-     *      single vs. multiple pictures (e.g., normal vs. picture-pile-style content)
-     */
-    class MultiPictureDraw : public GM {
-    public:
-        enum Content {
-            kNoClipSingle_Content,
-            kRectClipMulti_Content,
-            kRRectClipMulti_Content,
-            kPathClipMulti_Content,
-            kInvPathClipMulti_Content,
-            kSierpinski_Content,
-            kBigLayer_Content,
-
-            kLast_Content = kBigLayer_Content
-        };
-
-        const int kContentCnt = kLast_Content + 1;
-
-        enum Layout {
-            kSimple_Layout,
-            kTiled_Layout,
-
-            kLast_Layout = kTiled_Layout
-        };
-
-        const int kLayoutCnt = kLast_Layout + 1;
-
-        MultiPictureDraw(Content content, Layout layout) : fContent(content), fLayout(layout) {
-            SkASSERT(SK_ARRAY_COUNT(gLayoutMthds) == kLayoutCnt);
-            SkASSERT(SK_ARRAY_COUNT(gContentMthds) == kContentCnt);
-
-            for (int i = 0; i < kNumPictures; ++i) {
-                fPictures[i] = nullptr;
-            }
-        }
-
-        ~MultiPictureDraw() override {
-            for (int i = 0; i < kNumPictures; ++i) {
-                SkSafeUnref(fPictures[i]);
-            }
-        }
-
-    protected:
-        Content          fContent;
-        Layout           fLayout;
-        const SkPicture* fPictures[kNumPictures];
-
-        void onOnceBeforeDraw() override {
-            fPictures[0] = make_hex_plane_picture(SK_ColorWHITE).release();
-            fPictures[1] = make_hex_plane_picture(SK_ColorGRAY).release();
-            fPictures[2] = make_sierpinski_picture().release();
-            fPictures[3] = make_single_layer_hex_plane_picture().release();
-        }
-
-        void onDraw(SkCanvas* canvas) override {
-            SkMultiPictureDraw mpd;
-            SkTArray<ComposeStep> composeSteps;
-
-            // Fill up the MultiPictureDraw
-            (*gLayoutMthds[fLayout])(canvas, &mpd,
-                                     gContentMthds[fContent],
-                                     fPictures, &composeSteps);
-
-            mpd.draw();
-
-            // Compose all the drawn canvases into the final canvas
-            for (int i = 0; i < composeSteps.count(); ++i) {
-                const ComposeStep& step = composeSteps[i];
-
-                canvas->drawImage(step.fSurf->makeImageSnapshot().get(),
-                                  step.fX, step.fY, step.fPaint);
-            }
-        }
-
-        SkISize onISize() override { return SkISize::Make(kPicWidth, kPicHeight); }
-
-        SkString onShortName() override {
-            const char* gContentNames[] = {
-                "noclip", "rectclip", "rrectclip", "pathclip",
-                "invpathclip", "sierpinski", "biglayer"
-            };
-            const char* gLayoutNames[] = { "simple", "tiled" };
-
-            SkASSERT(SK_ARRAY_COUNT(gLayoutNames) == kLayoutCnt);
-            SkASSERT(SK_ARRAY_COUNT(gContentNames) == kContentCnt);
-
-            SkString name("multipicturedraw_");
-
-            name.append(gContentNames[fContent]);
-            name.append("_");
-            name.append(gLayoutNames[fLayout]);
-            return name;
-        }
-
-        bool runAsBench() const override { return true; }
-
-    private:
-        typedef GM INHERITED;
-    };
-
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kNoClipSingle_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRectClipMulti_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRRectClipMulti_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kPathClipMulti_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kInvPathClipMulti_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kSierpinski_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kBigLayer_Content,
-                                       MultiPictureDraw::kSimple_Layout);)
-
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kNoClipSingle_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRectClipMulti_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kRRectClipMulti_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kPathClipMulti_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kInvPathClipMulti_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kSierpinski_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-    DEF_GM(return new MultiPictureDraw(MultiPictureDraw::kBigLayer_Content,
-                                       MultiPictureDraw::kTiled_Layout);)
-}
diff --git a/gm/nested.cpp b/gm/nested.cpp
index 859077f..9c1370c 100644
--- a/gm/nested.cpp
+++ b/gm/nested.cpp
@@ -53,7 +53,7 @@
         kShapeCount
     };
 
-    static void AddShape(SkPath* path, const SkRect& rect, Shapes shape, SkPath::Direction dir) {
+    static void AddShape(SkPath* path, const SkRect& rect, Shapes shape, SkPathDirection dir) {
         switch (shape) {
             case kRect_Shape:
                 path->addRect(rect, dir);
@@ -105,9 +105,9 @@
                 for (size_t innerRect = 0; innerRect < SK_ARRAY_COUNT(innerRects); ++innerRect) {
                     SkPath path;
 
-                    AddShape(&path, outerRect, (Shapes) outerShape, SkPath::kCW_Direction);
+                    AddShape(&path, outerRect, (Shapes) outerShape, SkPathDirection::kCW);
                     AddShape(&path, innerRects[innerRect], (Shapes) innerShape,
-                             SkPath::kCCW_Direction);
+                             SkPathDirection::kCCW);
 
                     canvas->save();
                     if (fFlipped) {
diff --git a/gm/pathcontourstart.cpp b/gm/pathcontourstart.cpp
index 430e59b..7d3ff48 100644
--- a/gm/pathcontourstart.cpp
+++ b/gm/pathcontourstart.cpp
@@ -55,19 +55,19 @@
 
     void onDraw(SkCanvas* canvas) override {
 
-        drawDirs(canvas, [](const SkRect& rect, SkPath::Direction dir, unsigned startIndex) {
+        drawDirs(canvas, [](const SkRect& rect, SkPathDirection dir, unsigned startIndex) {
             SkPath path;
             path.addRect(rect, dir, startIndex);
             return path;
         });
 
-        drawDirs(canvas, [](const SkRect& rect, SkPath::Direction dir, unsigned startIndex) {
+        drawDirs(canvas, [](const SkRect& rect, SkPathDirection dir, unsigned startIndex) {
             SkPath path;
             path.addOval(rect, dir, startIndex);
             return path;
         });
 
-        drawDirs(canvas, [](const SkRect& rect, SkPath::Direction dir, unsigned startIndex) {
+        drawDirs(canvas, [](const SkRect& rect, SkPathDirection dir, unsigned startIndex) {
             SkRRect rrect;
             const SkVector radii[4] = { {15, 15}, {15, 15}, {15, 15}, {15, 15}};
             rrect.setRectRadii(rect, radii);
@@ -77,7 +77,7 @@
             return path;
         });
 
-        drawDirs(canvas, [](const SkRect& rect, SkPath::Direction dir, unsigned startIndex) {
+        drawDirs(canvas, [](const SkRect& rect, SkPathDirection dir, unsigned startIndex) {
             SkRRect rrect;
             rrect.setRect(rect);
 
@@ -86,7 +86,7 @@
             return path;
         });
 
-        drawDirs(canvas, [](const SkRect& rect, SkPath::Direction dir, unsigned startIndex) {
+        drawDirs(canvas, [](const SkRect& rect, SkPathDirection dir, unsigned startIndex) {
             SkRRect rrect;
             rrect.setOval(rect);
 
@@ -105,15 +105,15 @@
     SkRect  fRect;
 
     void drawDirs(SkCanvas* canvas,
-                  SkPath (*makePath)(const SkRect&, SkPath::Direction, unsigned)) const {
-        drawOneColumn(canvas, SkPath::kCW_Direction, makePath);
+                  SkPath (*makePath)(const SkRect&, SkPathDirection, unsigned)) const {
+        drawOneColumn(canvas, SkPathDirection::kCW, makePath);
         canvas->translate(kImageWidth / 10, 0);
-        drawOneColumn(canvas, SkPath::kCCW_Direction, makePath);
+        drawOneColumn(canvas, SkPathDirection::kCCW, makePath);
         canvas->translate(kImageWidth / 10, 0);
     }
 
-    void drawOneColumn(SkCanvas* canvas, SkPath::Direction dir,
-                       SkPath (*makePath)(const SkRect&, SkPath::Direction, unsigned)) const {
+    void drawOneColumn(SkCanvas* canvas, SkPathDirection dir,
+                       SkPath (*makePath)(const SkRect&, SkPathDirection, unsigned)) const {
         SkAutoCanvasRestore acr(canvas, true);
 
         for (unsigned i = 0; i < 8; ++i) {
diff --git a/gm/patheffects.cpp b/gm/patheffects.cpp
index 33a6841..37715f1 100644
--- a/gm/patheffects.cpp
+++ b/gm/patheffects.cpp
@@ -148,9 +148,9 @@
 
         path.reset();
         SkRect r = { 0, 0, 250, 120 };
-        path.addOval(r, SkPath::kCW_Direction);
+        path.addOval(r, SkPathDirection::kCW);
         r.inset(50, 50);
-        path.addRect(r, SkPath::kCCW_Direction);
+        path.addRect(r, SkPathDirection::kCCW);
 
         canvas->translate(320, 20);
         for (size_t i = 0; i < SK_ARRAY_COUNT(gPE2); i++) {
diff --git a/gm/pathinterior.cpp b/gm/pathinterior.cpp
index b152f72..eb83d93 100644
--- a/gm/pathinterior.cpp
+++ b/gm/pathinterior.cpp
@@ -75,9 +75,9 @@
                         for (int outerCW = 0; outerCW <= 1; ++outerCW) {
                             for (int innerCW = 0; innerCW <= 1; ++innerCW) {
                                 SkPath path;
-                                path.setFillType(doEvenOdd ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
-                                SkPath::Direction outerDir = outerCW ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
-                                SkPath::Direction innerDir = innerCW ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+                                path.setFillType(doEvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding);
+                                SkPathDirection outerDir = outerCW ? SkPathDirection::kCW : SkPathDirection::kCCW;
+                                SkPathDirection innerDir = innerCW ? SkPathDirection::kCW : SkPathDirection::kCCW;
 
                                 SkRect r = insetFirst ? inset(rect) : rect;
                                 if (outerRR) {
diff --git a/gm/pathmaskcache.cpp b/gm/pathmaskcache.cpp
index 8014275..07642fe 100644
--- a/gm/pathmaskcache.cpp
+++ b/gm/pathmaskcache.cpp
@@ -86,7 +86,7 @@
         paths.push_back();
         paths.back().addCircle(30.f, 30.f, 30.f);
         paths.back().addRect(SkRect::MakeXYWH(45.f, 45.f, 50.f, 60.f));
-        paths.back().setFillType(SkPath::kEvenOdd_FillType);
+        paths.back().setFillType(SkPathFillType::kEvenOdd);
 
         canvas->translate(kPad, kPad);
 
diff --git a/gm/pathopsinverse.cpp b/gm/pathopsinverse.cpp
index 60f80d0..54c494b 100644
--- a/gm/pathopsinverse.cpp
+++ b/gm/pathopsinverse.cpp
@@ -69,11 +69,11 @@
         SkPath one, two;
         int yPos = 0;
         for (int oneFill = 0; oneFill <= 1; ++oneFill) {
-            SkPath::FillType oneF = oneFill ? SkPath::kInverseEvenOdd_FillType
-                    : SkPath::kEvenOdd_FillType;
+            SkPathFillType oneF = oneFill ? SkPathFillType::kInverseEvenOdd
+                    : SkPathFillType::kEvenOdd;
             for (int twoFill = 0; twoFill <= 1; ++twoFill) {
-                SkPath::FillType twoF = twoFill ? SkPath::kInverseEvenOdd_FillType
-                        : SkPath::kEvenOdd_FillType;
+                SkPathFillType twoF = twoFill ? SkPathFillType::kInverseEvenOdd
+                        : SkPathFillType::kEvenOdd;
                 one.reset();
                 one.setFillType(oneF);
                 one.addRect(10, 10, 70, 70);
diff --git a/gm/polygonoffset.cpp b/gm/polygonoffset.cpp
index 9cdd9ea..9f906ac 100644
--- a/gm/polygonoffset.cpp
+++ b/gm/polygonoffset.cpp
@@ -23,12 +23,12 @@
 #include <functional>
 #include <memory>
 
-static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
+static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPathDirection dir) {
     float angleStep = 360.0f / n, angle = 0.0f;
     if ((n % 2) == 1) {
         angle = angleStep/2.0f;
     }
-    if (SkPath::kCCW_Direction == dir) {
+    if (SkPathDirection::kCCW == dir) {
         angle = -angle;
         angleStep = -angleStep;
     }
@@ -438,13 +438,13 @@
     SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
     bool runAsBench() const override { return true; }
 
-    static void GetConvexPolygon(int index, SkPath::Direction dir,
+    static void GetConvexPolygon(int index, SkPathDirection dir,
                                  std::unique_ptr<SkPoint[]>* data, int* numPts) {
         if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gConvexPoints)) {
             // manually specified
             *numPts = (int)PolygonOffsetData::gConvexSizes[index];
             data->reset(new SkPoint[*numPts]);
-            if (SkPath::kCW_Direction == dir) {
+            if (SkPathDirection::kCW == dir) {
                 for (int i = 0; i < *numPts; ++i) {
                     (*data)[i] = PolygonOffsetData::gConvexPoints[index][i];
                 }
@@ -473,13 +473,13 @@
         }
     }
 
-    static void GetSimplePolygon(int index, SkPath::Direction dir,
+    static void GetSimplePolygon(int index, SkPathDirection dir,
                                  std::unique_ptr<SkPoint[]>* data, int* numPts) {
         if (index < (int)SK_ARRAY_COUNT(PolygonOffsetData::gSimplePoints)) {
             // manually specified
             *numPts = (int)PolygonOffsetData::gSimpleSizes[index];
             data->reset(new SkPoint[*numPts]);
-            if (SkPath::kCW_Direction == dir) {
+            if (SkPathDirection::kCW == dir) {
                 for (int i = 0; i < *numPts; ++i) {
                     (*data)[i] = PolygonOffsetData::gSimplePoints[index][i];
                 }
@@ -515,9 +515,9 @@
             std::unique_ptr<SkPoint[]> data(nullptr);
             int numPts;
             if (fConvexOnly) {
-                GetConvexPolygon(index, SkPath::kCW_Direction, &data, &numPts);
+                GetConvexPolygon(index, SkPathDirection::kCW, &data, &numPts);
             } else {
-                GetSimplePolygon(index, SkPath::kCW_Direction, &data, &numPts);
+                GetSimplePolygon(index, SkPathDirection::kCW, &data, &numPts);
             }
             bounds.setBounds(data.get(), numPts);
             if (!fConvexOnly) {
@@ -531,7 +531,7 @@
             offset->fX += bounds.width();
         }
 
-        const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
+        const SkPathDirection dirs[2] = { SkPathDirection::kCW, SkPathDirection::kCCW };
         const float insets[] = { 5, 10, 15, 20, 25, 30, 35, 40 };
         const float offsets[] = { 2, 5, 9, 14, 20, 27, 35, 44, -2, -5, -9 };
         const SkColor colors[] = { 0xFF901313, 0xFF8D6214, 0xFF698B14, 0xFF1C8914,
diff --git a/gm/preservefillrule.cpp b/gm/preservefillrule.cpp
index dfbed2d..26ff7f6 100644
--- a/gm/preservefillrule.cpp
+++ b/gm/preservefillrule.cpp
@@ -82,19 +82,19 @@
 
         auto starRect = SkRect::MakeWH(fStarSize, fStarSize);
         SkPath star7_winding = ToolUtils::make_star(starRect, 7);
-        star7_winding.setFillType(SkPath::kWinding_FillType);
+        star7_winding.setFillType(SkPathFillType::kWinding);
 
         SkPath star7_evenOdd = star7_winding;
         star7_evenOdd.transform(SkMatrix::MakeTrans(0, fStarSize));
-        star7_evenOdd.setFillType(SkPath::kEvenOdd_FillType);
+        star7_evenOdd.setFillType(SkPathFillType::kEvenOdd);
 
         SkPath star5_winding = ToolUtils::make_star(starRect, 5);
         star5_winding.transform(SkMatrix::MakeTrans(fStarSize, 0));
-        star5_winding.setFillType(SkPath::kWinding_FillType);
+        star5_winding.setFillType(SkPathFillType::kWinding);
 
         SkPath star5_evenOdd = star5_winding;
         star5_evenOdd.transform(SkMatrix::MakeTrans(0, fStarSize));
-        star5_evenOdd.setFillType(SkPath::kEvenOdd_FillType);
+        star5_evenOdd.setFillType(SkPathFillType::kEvenOdd);
 
         SkPaint paint;
         paint.setColor(SK_ColorGREEN);
diff --git a/gm/quadpaths.cpp b/gm/quadpaths.cpp
index 359661f..8570934 100644
--- a/gm/quadpaths.cpp
+++ b/gm/quadpaths.cpp
@@ -36,7 +36,7 @@
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
+                  SkPaint::Style style, SkPathFillType fill,
                   SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -53,14 +53,14 @@
 
     void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
@@ -166,7 +166,7 @@
 
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
-                  SkPaint::Style style, SkPath::FillType fill,
+                  SkPaint::Style style, SkPathFillType fill,
                   SkScalar strokeWidth) {
         path.setFillType(fill);
         SkPaint paint;
@@ -183,14 +183,14 @@
 
     void onDraw(SkCanvas* canvas) override {
         struct FillAndName {
-            SkPath::FillType fFill;
+            SkPathFillType fFill;
             const char*      fName;
         };
         constexpr FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
+            {SkPathFillType::kWinding, "Winding"},
+            {SkPathFillType::kEvenOdd, "Even / Odd"},
+            {SkPathFillType::kInverseWinding, "Inverse Winding"},
+            {SkPathFillType::kInverseEvenOdd, "Inverse Even / Odd"},
         };
         struct StyleAndName {
             SkPaint::Style fStyle;
diff --git a/gm/samplelocations.cpp b/gm/samplelocations.cpp
index eeba571..79c0c65 100644
--- a/gm/samplelocations.cpp
+++ b/gm/samplelocations.cpp
@@ -53,6 +53,7 @@
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 #include "src/gpu/ops/GrDrawOp.h"
 #include "src/gpu/ops/GrOp.h"
+#include "tools/gpu/ProxyUtils.h"
 
 #include <memory>
 #include <utility>
@@ -97,21 +98,32 @@
 
 class SampleLocationsTestProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, GradType gradType) {
+        return arena->make<SampleLocationsTestProcessor>(gradType);
+    }
+
+    const char* name() const override { return "SampleLocationsTestProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
+        b->add32((uint32_t)fGradType);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     SampleLocationsTestProcessor(GradType gradType)
             : GrGeometryProcessor(kSampleLocationsTestProcessor_ClassID)
             , fGradType(gradType) {
         this->setWillUseCustomFeature(CustomFeatures::kSampleLocations);
     }
-    const char* name() const override { return "SampleLocationsTestProcessor"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
-        b->add32((uint32_t)fGradType);
-    }
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
 
-private:
     const GradType fGradType;
 
     class Impl;
+
+    typedef GrGeometryProcessor INHERITED;
 };
 
 class SampleLocationsTestProcessor::Impl : public GrGLSLGeometryProcessor {
@@ -185,7 +197,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&&) override {}
+                 const CoordTransformRange&) override {}
 };
 
 GrGLSLPrimitiveProcessor* SampleLocationsTestProcessor::createGLSLInstance(
@@ -196,6 +208,16 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Draw Op.
 
+static constexpr GrUserStencilSettings gStencilWrite(
+    GrUserStencilSettings::StaticInit<
+        0x0001,
+        GrUserStencilTest::kAlways,
+        0xffff,
+        GrUserStencilOp::kReplace,
+        GrUserStencilOp::kKeep,
+        0xffff>()
+);
+
 class SampleLocationsTestOp : public GrDrawOp {
 public:
     DEFINE_OP_CLASS_ID
@@ -219,38 +241,65 @@
                                       bool hasMixedSampledCoverage, GrClampType) override {
         return GrProcessorSet::EmptySetAnalysis();
     }
-    void onPrepare(GrOpFlushState*) override {}
-    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
-        static constexpr GrUserStencilSettings kStencilWrite(
-            GrUserStencilSettings::StaticInit<
-                0x0001,
-                GrUserStencilTest::kAlways,
-                0xffff,
-                GrUserStencilOp::kReplace,
-                GrUserStencilOp::kKeep,
-                0xffff>()
-        );
 
-        GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
-                            flushState->drawOpArgs().outputSwizzle(),
-                            GrPipeline::InputFlags::kHWAntialias, &kStencilWrite);
+    void onPrePrepare(GrRecordingContext* context,
+                      const GrSurfaceProxyView* dstView,
+                      GrAppliedClip* clip,
+                      const GrXferProcessor::DstProxyView& dstProxyView) final {
+        // We're going to create the GrProgramInfo (and the GrPipeline and geometry processor
+        // it relies on) in the DDL-record-time arena.
+        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
 
-        SampleLocationsTestProcessor primProc(fGradType);
+        // This is equivalent to a GrOpFlushState::detachAppliedClip
+        GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
 
-        GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                                  flushState->drawOpArgs().origin(),
-                                  pipeline,
-                                  primProc,
-                                  nullptr, nullptr, 0);
+        GrGeometryProcessor* geomProc = SampleLocationsTestProcessor::Make(arena, fGradType);
+
+        fProgramInfo = sk_gpu_test::CreateProgramInfo(context->priv().caps(), arena, dstView,
+                                                      std::move(appliedClip), dstProxyView,
+                                                      geomProc, SkBlendMode::kSrcOver,
+                                                      GrPrimitiveType::kTriangleStrip,
+                                                      GrPipeline::InputFlags::kHWAntialias,
+                                                      &gStencilWrite);
+    }
+
+    void onPrepare(GrOpFlushState*) final {}
+
+    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final {
+
+        if (!fProgramInfo) {
+            auto geomProc = SampleLocationsTestProcessor::Make(flushState->allocator(),
+                                                               fGradType);
+
+            fProgramInfo = sk_gpu_test::CreateProgramInfo(&flushState->caps(),
+                                                          flushState->allocator(),
+                                                          flushState->view(),
+                                                          flushState->detachAppliedClip(),
+                                                          flushState->dstProxyView(),
+                                                          geomProc, SkBlendMode::kSrcOver,
+                                                          GrPrimitiveType::kTriangleStrip,
+                                                          GrPipeline::InputFlags::kHWAntialias,
+                                                          &gStencilWrite);
+        }
 
         GrMesh mesh(GrPrimitiveType::kTriangleStrip);
         mesh.setInstanced(nullptr, 200*200, 0, 4);
-        flushState->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::MakeIWH(200, 200));
+
+        flushState->opsRenderPass()->draw(*fProgramInfo, &mesh, 1, SkRect::MakeIWH(200, 200));
     }
 
     const GradType fGradType;
 
+    // The program info (and both the GrPipeline and GrPrimitiveProcessor it relies on), when
+    // allocated, are allocated in either the ddl-record-time or flush-time arena. It is the
+    // arena's job to free up their memory so we just have a bare programInfo pointer here. We
+    // don't even store the GrPipeline and GrPrimitiveProcessor pointers here bc they are
+    // guaranteed to have the same lifetime as the program info.
+    GrProgramInfo*  fProgramInfo = nullptr;
+
     friend class ::GrOpMemoryPool; // for ctor
+
+    typedef GrDrawOp INHERITED;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -262,8 +311,8 @@
         *errorMsg = "Requires support for sample locations.";
         return DrawResult::kSkip;
     }
-    if (!ctx->priv().caps()->shaderCaps()->sampleVariablesSupport()) {
-        *errorMsg = "Requires support for sample variables.";
+    if (!ctx->priv().caps()->shaderCaps()->sampleMaskSupport()) {
+        *errorMsg = "Requires support for sample mask.";
         return DrawResult::kSkip;
     }
     if (rtc->numSamples() <= 1 && !ctx->priv().caps()->mixedSamplesSupport()) {
@@ -308,12 +357,12 @@
                             SkMatrix::I(), SkRect::MakeWH(200, 200));
 
     // Copy offscreen texture to canvas.
-    rtc->drawTexture(
-            GrNoClip(), sk_ref_sp(offscreenRTC->asTextureProxy()),
-            offscreenRTC->colorInfo().colorType(), GrSamplerState::Filter::kNearest,
-            SkBlendMode::kSrc, SK_PMColor4fWHITE, {0,0,200,200}, {0,0,200,200}, GrAA::kNo,
-            GrQuadAAFlags::kNone, SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint,
-            SkMatrix::I(), nullptr);
+    rtc->drawTexture(GrNoClip(), sk_ref_sp(offscreenRTC->asTextureProxy()),
+                     offscreenRTC->colorInfo().colorType(), offscreenRTC->colorInfo().alphaType(),
+                     GrSamplerState::Filter::kNearest, SkBlendMode::kSrc, SK_PMColor4fWHITE,
+                     {0,0,200,200}, {0,0,200,200}, GrAA::kNo, GrQuadAAFlags::kNone,
+                     SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
+                     nullptr);
 
     return skiagm::DrawResult::kOk;
 }
diff --git a/gm/sharedcorners.cpp b/gm/sharedcorners.cpp
index 7fc8fd1..88323b8 100644
--- a/gm/sharedcorners.cpp
+++ b/gm/sharedcorners.cpp
@@ -112,7 +112,7 @@
     void drawTriangleBoxes(SkCanvas* canvas, const std::vector<SkPoint>& points,
                            const std::vector<std::array<int, 3>>& triangles) {
         SkPath path;
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
         path.setIsVolatile(true);
         for (const std::array<int, 3>& triangle : triangles) {
             path.moveTo(points[triangle[0]]);
diff --git a/gm/skbug1719.cpp b/gm/skbug1719.cpp
index 177b903..1f4ec10 100644
--- a/gm/skbug1719.cpp
+++ b/gm/skbug1719.cpp
@@ -59,7 +59,7 @@
         drawPath.lineTo(824.f, 662.f);
         drawPath.cubicTo(824.f, 657.58173f, 827.58173f, 654.f, 832.f, 654.f);
         drawPath.close();
-        drawPath.setFillType(SkPath::kEvenOdd_FillType);
+        drawPath.setFillType(SkPathFillType::kEvenOdd);
 
         SkPaint paint;
         paint.setAntiAlias(true);
diff --git a/gm/skvm.cpp b/gm/skvm.cpp
new file mode 100644
index 0000000..28d7d0d
--- /dev/null
+++ b/gm/skvm.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "gm/gm.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkShader.h"
+#include "include/effects/SkColorMatrix.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+#include "src/shaders/SkShaderBase.h"
+
+// This GM exercises interesting cases in SkVMBlitter,
+// and mostly draws uninteresting simple shapes and colors.
+// At the moment it's really only interesting if you #define SK_USE_SKVM_BLITTER.
+
+// Just a tiny example that the (x,y) coordinate parameters are vaguely working.
+// In this case we'll fade the red channel over its span vertically using `y`,
+// and green horizontally using `x`.
+struct Fade : public SkShaderBase {
+    explicit Fade(sk_sp<SkShader> shader) : fShader(std::move(shader)) {}
+
+    sk_sp<SkShader> fShader;
+
+    bool isOpaque() const override { return fShader->isOpaque(); }
+
+    bool onProgram(skvm::Builder* p,
+                   SkColorSpace* dstCS,
+                   skvm::Uniforms* uniforms,
+                   skvm::F32 x, skvm::F32 y,
+                   skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override {
+        if (as_SB(fShader)->program(p, dstCS,
+                                    uniforms,
+                                    x,y, r,g,b,a)) {
+            // In this GM `y` will range over 0-50 and `x` over 50-100.
+            *r = p->mul(y, p->splat(1/ 50.0f));
+            *g = p->mul(x, p->splat(1/100.0f));
+            return true;
+        }
+        return false;
+    }
+
+    // Flattening is not really necessary, just nice to make serialize-8888 etc. not crash.
+    void flatten(SkWriteBuffer& b) const override {
+        b.writeFlattenable(fShader.get());
+    }
+    SK_FLATTENABLE_HOOKS(Fade)
+};
+sk_sp<SkFlattenable> Fade::CreateProc(SkReadBuffer& b) {
+    return sk_make_sp<Fade>(b.readShader());
+}
+static struct RegisterFade {
+    RegisterFade() { SkFlattenable::Register("Fade", Fade::CreateProc); }
+} now;
+
+DEF_SIMPLE_GM(SkVMBlitter, canvas, 100, 150) {
+    SkPaint p;
+
+    // These draws are all supported by SkVMBlitter,
+    // and the some draws will reuse programs cached by earlier draws.
+    //
+    // We don't have any API to detect this, but you can flip on the #if guard
+    // around "calls to done" in SkVMBlitter.cpp and run this GM in isolation.
+    // You should see 2 call to done.
+    //
+    //    $ ninja -C out fm && out/fm -b cpu -s SkVMBlitter
+    //    ...
+    //    2 calls to done
+    //
+    // The first program is actually created when the GM framework first
+    // clears the buffer white by setting a paint color to SK_ColorWHITE like
+    // we do with SK_ColorRED.  SkVMBlitter is clever enough now to build the
+    // same program for paint colors or color shaders.
+    //
+    // The second program handles the draw with the Fade shader.
+
+    /*
+    4 registers, 9 instructions:
+    r0 = uniform32 arg(0) 8
+    r1 = splat FF (3.5733111e-43)
+    r2 = extract r0 0 r1
+    r3 = extract r0 8 r1
+    r3 = pack r2 r3 8
+    r0 = extract r0 16 r1
+    r1 = pack r0 r1 8
+    r1 = pack r3 r1 16
+    loop:
+        store32 arg(1) r1
+    */
+    p.setShader(SkShaders::Color(SK_ColorBLUE));
+    canvas->drawRect({0,0, 50,50}, p);
+
+    p.setShader(SkShaders::Color(SK_ColorGREEN));
+    canvas->drawRect({50,50, 100,100}, p);
+
+    p.setShader(nullptr);
+    p.setColor(SK_ColorRED);
+    canvas->drawRect({0,50, 50,100}, p);
+
+    /*
+    5 registers, 19 instructions:
+    r0 = uniform32 arg(0) 4                    // load y
+    r0 = to_f32 r0
+    r1 = splat 40A33333 (5.0999999)
+    r1 = mul_f32 r0 r1
+    r1 = to_i32 r1                             // r1 = red channel, depends on y, is uniform
+    r0 = uniform32 arg(0) 0                    // load right edge, used to calculate x in loop
+    r2 = splat 40233333 (2.55)
+    r3 = splat FF (3.5733111e-43)              // color shader alpha, known to be opaque
+    r4 = uniform32 arg(0) 8                    // load color shader for blue
+    r4 = extract r4 16 r3                      // extract blue
+    r3 = pack r4 r3 8                          // r3 = blue and alpha from color shader
+    loop:
+        r4 = index
+        r4 = sub_i32 r0 r4                     // r4 = x
+        r4 = to_f32 r4
+        r4 = mul_f32 r4 r2
+        r4 = to_i32 r4                         // r4 = green channel, depends on x, is varying
+        r4 = pack r1 r4 8
+        r4 = pack r4 r3 16
+        store32 arg(1) r4
+    */
+    p.setShader(sk_make_sp<Fade>(SkShaders::Color(SK_ColorYELLOW)));
+    canvas->drawRect({50,0, 100,50}, p);
+
+    // Draw with color filter, w/ and w/o alpha modulation.
+    SkColorMatrix m;
+    m.setSaturation(0.3f);
+
+    p.setShader(SkShaders::Color(SK_ColorMAGENTA));
+    p.setColorFilter(SkColorFilters::Matrix(m));
+    canvas->drawRect({0,100, 50,150}, p);
+
+    p.setShader(SkShaders::Color(0xffffaa00));  // tan
+    p.setColorFilter(SkColorFilters::Matrix(m));
+    p.setAlphaf(0.5f);
+    canvas->drawRect({25,100, 75,150}, p); // overlap a bit with purple and white
+}
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
index 9b0bb04..095da77 100644
--- a/gm/strokefill.cpp
+++ b/gm/strokefill.cpp
@@ -282,51 +282,51 @@
         paint.setStyle(SkPaint::kStrokeAndFill_Style);
 
         SkPath path;
-        path.setFillType(SkPath::kWinding_FillType);
-        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
-        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCCW_Direction);
+        path.setFillType(SkPathFillType::kWinding);
+        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(50), SkPathDirection::kCW);
+        path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(40), SkPathDirection::kCCW);
         canvas->drawPath(path, paint);
 
         SkPath path2;
-        path2.setFillType(SkPath::kWinding_FillType);
-        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
-        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCW_Direction);
+        path2.setFillType(SkPathFillType::kWinding);
+        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(50), SkPathDirection::kCCW);
+        path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(40), SkPathDirection::kCW);
         canvas->drawPath(path2, paint);
 
         path2.reset();
-        path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
+        path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPathDirection::kCCW);
         canvas->drawPath(path2, paint);
         SkASSERT(SkPathPriv::CheapIsFirstDirection(path2, SkPathPriv::kCCW_FirstDirection));
 
         path2.reset();
         SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path2, nullptr));
-        path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
+        path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPathDirection::kCW);
         SkASSERT(SkPathPriv::CheapIsFirstDirection(path2, SkPathPriv::kCW_FirstDirection));
         canvas->drawPath(path2, paint);
 
         SkRect r = SkRect::MakeXYWH(x - SkIntToScalar(50), y + SkIntToScalar(280),
                                     SkIntToScalar(100), SkIntToScalar(100));
         SkPath path3;
-        path3.setFillType(SkPath::kWinding_FillType);
-        path3.addRect(r, SkPath::kCW_Direction);
+        path3.setFillType(SkPathFillType::kWinding);
+        path3.addRect(r, SkPathDirection::kCW);
         r.inset(SkIntToScalar(10), SkIntToScalar(10));
-        path3.addRect(r, SkPath::kCCW_Direction);
+        path3.addRect(r, SkPathDirection::kCCW);
         canvas->drawPath(path3, paint);
 
         r = SkRect::MakeXYWH(x + SkIntToScalar(70), y + SkIntToScalar(280),
                              SkIntToScalar(100), SkIntToScalar(100));
         SkPath path4;
-        path4.setFillType(SkPath::kWinding_FillType);
-        path4.addRect(r, SkPath::kCCW_Direction);
+        path4.setFillType(SkPathFillType::kWinding);
+        path4.addRect(r, SkPathDirection::kCCW);
         r.inset(SkIntToScalar(10), SkIntToScalar(10));
-        path4.addRect(r, SkPath::kCW_Direction);
+        path4.addRect(r, SkPathDirection::kCW);
         canvas->drawPath(path4, paint);
 
         r = SkRect::MakeXYWH(x + SkIntToScalar(190), y + SkIntToScalar(280),
                              SkIntToScalar(100), SkIntToScalar(100));
         path4.reset();
         SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path4, nullptr));
-        path4.addRect(r, SkPath::kCCW_Direction);
+        path4.addRect(r, SkPathDirection::kCCW);
         SkASSERT(SkPathPriv::CheapIsFirstDirection(path4, SkPathPriv::kCCW_FirstDirection));
         path4.moveTo(0, 0); // test for crbug.com/247770
         canvas->drawPath(path4, paint);
@@ -335,7 +335,7 @@
                              SkIntToScalar(100), SkIntToScalar(100));
         path4.reset();
         SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path4, nullptr));
-        path4.addRect(r, SkPath::kCW_Direction);
+        path4.addRect(r, SkPathDirection::kCW);
         SkASSERT(SkPathPriv::CheapIsFirstDirection(path4, SkPathPriv::kCW_FirstDirection));
         path4.moveTo(0, 0); // test for crbug.com/247770
         canvas->drawPath(path4, paint);
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index a2e7bfd..9b3b1a4 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -344,42 +344,42 @@
 
 class Strokes3GM : public skiagm::GM {
     static void make0(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addRect(bounds, SkPath::kCW_Direction);
-        path->addRect(inset(bounds), SkPath::kCW_Direction);
+        path->addRect(bounds, SkPathDirection::kCW);
+        path->addRect(inset(bounds), SkPathDirection::kCW);
         title->set("CW CW");
     }
 
     static void make1(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addRect(bounds, SkPath::kCW_Direction);
-        path->addRect(inset(bounds), SkPath::kCCW_Direction);
+        path->addRect(bounds, SkPathDirection::kCW);
+        path->addRect(inset(bounds), SkPathDirection::kCCW);
         title->set("CW CCW");
     }
 
     static void make2(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addOval(bounds, SkPath::kCW_Direction);
-        path->addOval(inset(bounds), SkPath::kCW_Direction);
+        path->addOval(bounds, SkPathDirection::kCW);
+        path->addOval(inset(bounds), SkPathDirection::kCW);
         title->set("CW CW");
     }
 
     static void make3(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addOval(bounds, SkPath::kCW_Direction);
-        path->addOval(inset(bounds), SkPath::kCCW_Direction);
+        path->addOval(bounds, SkPathDirection::kCW);
+        path->addOval(inset(bounds), SkPathDirection::kCCW);
         title->set("CW CCW");
     }
 
     static void make4(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addRect(bounds, SkPath::kCW_Direction);
+        path->addRect(bounds, SkPathDirection::kCW);
         SkRect r = bounds;
         r.inset(bounds.width() / 10, -bounds.height() / 10);
-        path->addOval(r, SkPath::kCW_Direction);
+        path->addOval(r, SkPathDirection::kCW);
         title->set("CW CW");
     }
 
     static void make5(SkPath* path, const SkRect& bounds, SkString* title) {
-        path->addRect(bounds, SkPath::kCW_Direction);
+        path->addRect(bounds, SkPathDirection::kCW);
         SkRect r = bounds;
         r.inset(bounds.width() / 10, -bounds.height() / 10);
-        path->addOval(r, SkPath::kCCW_Direction);
+        path->addOval(r, SkPathDirection::kCCW);
         title->set("CW CCW");
     }
 
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index 5355e2e..e7d08cd 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -9,48 +9,35 @@
 
 #include "gm/gm.h"
 #include "include/core/SkBitmap.h"
-#include "include/core/SkBlendMode.h"
 #include "include/core/SkCanvas.h"
 #include "include/core/SkColor.h"
 #include "include/core/SkMatrix.h"
 #include "include/core/SkPaint.h"
 #include "include/core/SkRect.h"
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkScalar.h"
-#include "include/core/SkShader.h"
 #include "include/core/SkSize.h"
 #include "include/core/SkString.h"
-#include "include/core/SkTypes.h"
 #include "include/effects/SkGradientShader.h"
-#include "include/gpu/GrContext.h"
-#include "include/gpu/GrTypes.h"
 #include "include/private/GrTypesPriv.h"
 #include "include/private/SkTArray.h"
-#include "src/gpu/GrCaps.h"
 #include "src/gpu/GrContextPriv.h"
-#include "src/gpu/GrFragmentProcessor.h"
-#include "src/gpu/GrPaint.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrSamplerState.h"
 #include "src/gpu/GrTextureProxy.h"
-#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
 #include "src/gpu/effects/GrTextureDomain.h"
-#include "src/gpu/ops/GrDrawOp.h"
-#include "src/gpu/ops/GrFillRectOp.h"
+#include "tools/gpu/TestOps.h"
 
 #include <memory>
 #include <utility>
 
 namespace skiagm {
 /**
- * This GM directly exercises GrTextureDomainEffect.
+ * This GM directly exercises GrDomainEffect.
  */
 class TextureDomainEffect : public GpuGM {
 public:
-    TextureDomainEffect(GrSamplerState::Filter filter)
-            : fFilter(filter) {
+    TextureDomainEffect(GrSamplerState::Filter filter) : fFilter(filter) {
         this->setBGColor(0xFFFFFFFF);
     }
 
@@ -66,9 +53,9 @@
     }
 
     SkISize onISize() override {
-        const SkScalar canvasWidth = kDrawPad +
-                (kTargetWidth + 2 * kDrawPad) * GrTextureDomain::kModeCount +
-                kTestPad * GrTextureDomain::kModeCount;
+        const SkScalar canvasWidth =
+                kDrawPad + 2 * ((kTargetWidth + 2 * kDrawPad) * GrTextureDomain::kModeCount +
+                                kTestPad * GrTextureDomain::kModeCount);
         return SkISize::Make(SkScalarCeilToInt(canvasWidth), 800);
     }
 
@@ -124,8 +111,7 @@
                               fBitmap.width() / 2 + 2, fBitmap.height() / 2 + 2),
         };
 
-        SkRect renderRect = SkRect::Make(fBitmap.bounds());
-        renderRect.outset(kDrawPad, kDrawPad);
+        SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad);
 
         SkScalar y = kDrawPad + kTestPad;
         for (int tm = 0; tm < textureMatrices.count(); ++tm) {
@@ -138,26 +124,35 @@
                         // Repeat mode doesn't produce correct results with bilerp filtering
                         continue;
                     }
-
-                    GrPaint grPaint;
-                    grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
-                    auto fp = GrTextureDomainEffect::Make(
-                            proxy, SkColorTypeToGrColorType(fBitmap.colorType()),
-                            textureMatrices[tm],
-                            GrTextureDomain::MakeTexelDomain(texelDomains[d], mode),
+                    auto fp1 = GrSimpleTextureEffect::Make(proxy, fBitmap.alphaType(),
+                                                           textureMatrices[tm], fFilter);
+                    fp1 = GrDomainEffect::Make(
+                            std::move(fp1), GrTextureDomain::MakeTexelDomain(texelDomains[d], mode),
                             mode, fFilter);
-
-                    if (!fp) {
+                    if (!fp1) {
                         continue;
                     }
-                    const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y);
-                    grPaint.addColorFragmentProcessor(std::move(fp));
-                    renderTargetContext->priv().testingOnly_addDrawOp(
-                            GrFillRectOp::MakeNonAARect(context, std::move(grPaint),
-                                                        viewMatrix, renderRect));
-                    x += renderRect.width() + kTestPad;
+                    auto fp2 = fp1->clone();
+                    SkASSERT(fp2);
+                    auto drawRect = localRect.makeOffset(x, y);
+                    if (auto op = sk_gpu_test::test_ops::MakeRect(
+                                context, std::move(fp1), drawRect, localRect)) {
+                        renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+                    }
+                    x += localRect.width() + kTestPad;
+                    // Draw again with a translated local rect and compensating translate matrix.
+                    drawRect = localRect.makeOffset(x, y);
+                    static constexpr SkVector kT = {-100, 300};
+                    if (auto op = sk_gpu_test::test_ops::MakeRect(context,
+                                                                  std::move(fp2),
+                                                                  drawRect,
+                                                                  localRect.makeOffset(kT),
+                                                                  SkMatrix::MakeTrans(-kT))) {
+                        renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
+                    }
+                    x += localRect.width() + kTestPad;
                 }
-                y += renderRect.height() + kTestPad;
+                y += localRect.height() + kTestPad;
             }
         }
         return DrawResult::kOk;
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 0444279..4607a54 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -43,6 +43,7 @@
 #include "include/private/SkTDArray.h"
 #include "include/private/SkTemplates.h"
 #include "include/utils/SkTextUtils.h"
+#include "src/core/SkYUVMath.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpu.h"
 #include "tools/ToolUtils.h"
@@ -407,92 +408,24 @@
     return bm;
 }
 
-static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
-                                            uint8_t off, uint8_t range) {
-    static const float Kr = 0.299f;
-    static const float Kb = 0.114f;
-    static const float Kg = 1.0f - Kr - Kb;
+static void convert_rgba_to_yuva(const float mtx[20], SkColor col, uint8_t yuv[4]) {
+    const uint8_t r = SkColorGetR(col);
+    const uint8_t g = SkColorGetG(col);
+    const uint8_t b = SkColorGetB(col);
 
-    float r = SkColorGetR(col) / 255.0f;
-    float g = SkColorGetG(col) / 255.0f;
-    float b = SkColorGetB(col) / 255.0f;
-
-    float Ey = Kr * r + Kg * g + Kb * b;
-    float Ecb = (b - Ey) / 1.402f;
-    float Ecr = (r - Ey) / 1.772;
-    SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
-    SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
-    SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
-
-    yuv[0] = SkScalarRoundToInt( range * Ey + off );
-    yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
-    yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
+    yuv[0] = SkScalarPin(SkScalarRoundToInt(mtx[ 0]*r + mtx[ 1]*g + mtx[ 2]*b + mtx[ 4]*255), 0, 255);
+    yuv[1] = SkScalarPin(SkScalarRoundToInt(mtx[ 5]*r + mtx[ 6]*g + mtx[ 7]*b + mtx[ 9]*255), 0, 255);
+    yuv[2] = SkScalarPin(SkScalarRoundToInt(mtx[10]*r + mtx[11]*g + mtx[12]*b + mtx[14]*255), 0, 255);
     yuv[3] = SkColorGetA(col);
 }
 
-static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
-    // full swing from 0..255
-    convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
-}
+static SkPMColor convert_yuva_to_rgba(const float mtx[20],
+                                      uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
+    uint8_t r = SkScalarPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
+    uint8_t g = SkScalarPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
+    uint8_t b = SkScalarPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
 
-static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
-    // partial swing from 16..235
-    convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
-
-}
-
-static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
-    static const float Kr = 0.2126f;
-    static const float Kb = 0.0722f;
-    static const float Kg = 1.0f - Kr - Kb;
-
-    float r = SkColorGetR(col) / 255.0f;
-    float g = SkColorGetG(col) / 255.0f;
-    float b = SkColorGetB(col) / 255.0f;
-
-    float Ey = Kr * r + Kg * g + Kb * b;
-    float Ecb = (b - Ey) / 1.8556f;
-    float Ecr = (r - Ey) / 1.5748;
-    SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
-    SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
-    SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
-
-    yuv[0] = SkScalarRoundToInt( 219 * Ey +  16 );
-    yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
-    yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
-
-    yuv[3] = SkColorGetA(col);
-}
-
-
-static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
-    uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * y                   +  1.402f    * v  - 0.703749f * 255),
-                            0, 255);
-    uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * y - (0.344136f * u) - (0.714136f * v) + 0.531211f * 255),
-                            0, 255);
-    uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * y +  1.772f    * u                    - 0.889475f * 255),
-                            0, 255);
-
-    SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
-    return c;
-}
-
-static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
-    uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y                +  1.596f * v  - 0.87075f * 255), 0, 255);
-    uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.391f * u) - (0.813f * v) + 0.52925f * 255), 0, 255);
-    uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y +  2.018f * u                 - 1.08175f * 255), 0, 255);
-
-    SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
-    return c;
-}
-
-static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
-    uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y                + (1.793f * v) - 0.96925f * 255), 0, 255);
-    uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.213f * u) - (0.533f * v) + 0.30025f * 255), 0, 255);
-    uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (2.112f * u)                - 1.12875f * 255), 0, 255);
-
-    SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
-    return c;
+    return SkPremultiplyARGBInline(a, b, g, r);
 }
 
 static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
@@ -520,6 +453,9 @@
     planes->fQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
                                  kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
 
+    float mtx[20];
+    SkColorMatrix_RGB2YUV(yuvColorSpace, mtx);
+
     SkColor4f* dst = (SkColor4f *) planes->fFull.getAddr(0, 0);
     for (int y = 0; y < bm.height(); ++y) {
         for (int x = 0; x < bm.width(); ++x) {
@@ -527,14 +463,7 @@
 
             uint8_t yuva[4];
 
-            if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
-                convert_rgba_to_yuva_jpeg(col, yuva);
-            } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
-                convert_rgba_to_yuva_601(col, yuva);
-            } else {
-                SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
-                convert_rgba_to_yuva_709(col, yuva);
-            }
+            convert_rgba_to_yuva(mtx, col, yuva);
 
             *planes->fYFull.getAddr8(x, y) = yuva[0];
             *planes->fUFull.getAddr8(x, y) = yuva[1];
@@ -894,6 +823,9 @@
             fFlattened.allocPixels(info);
             SkASSERT(kPremul_SkAlphaType == info.alphaType());
 
+            float mtx[20];
+            SkColorMatrix_YUV2RGB(fYUVColorSpace, mtx);
+
             for (int y = 0; y < info.height(); ++y) {
                 for (int x = 0; x < info.width(); ++x) {
 
@@ -921,20 +853,7 @@
                     }
 
                     // Making premul here.
-                    switch (fYUVColorSpace) {
-                        case kJPEG_SkYUVColorSpace:
-                            *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
-                            break;
-                        case kRec601_SkYUVColorSpace:
-                            *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
-                            break;
-                        case kRec709_SkYUVColorSpace:
-                            *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
-                            break;
-                        case kIdentity_SkYUVColorSpace:
-                            *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, V, U, Y);
-                            break;
-                    }
+                    *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, Y, U, V, A);
                 }
             }
         }
@@ -999,7 +918,7 @@
 }
 
 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
-    static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
+    static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "2020", "Identity" };
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
 
     SkPaint paint;
@@ -1483,7 +1402,6 @@
 
 #include "include/effects/SkColorMatrix.h"
 #include "src/core/SkAutoPixmapStorage.h"
-#include "src/core/SkYUVMath.h"
 #include "tools/Resources.h"
 
 static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
@@ -1546,7 +1464,7 @@
     }
 
     SkISize onISize() override {
-        return SkISize::Make(1024, 768);
+        return SkISize::Make(1280, 768);
     }
 
     void onOnceBeforeDraw() override {
@@ -1575,7 +1493,8 @@
 
         canvas->translate(fOrig->width(), 0);
         canvas->save();
-        for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
+        for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace,
+                        kBT2020_SkYUVColorSpace}) {
             split_into_yuv(fOrig.get(), cs, fPM);
             auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
                                                     fPM[0].info().dimensions(),
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 248adf3..49c0c95 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -3,6 +3,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+if (is_fuchsia) {
+  import("//build/fuchsia/sdk.gni")
+}
+
 declare_args() {
   extra_asmflags = []
   extra_cflags = []
@@ -56,6 +60,26 @@
     }
   }
 
+  if (is_fuchsia && using_fuchsia_sdk) {
+    ldflags += [
+      "-v",
+      "--sysroot=" + rebase_path("$fuchsia_sdk_path/arch/$target_cpu/sysroot"),
+    ]
+    cflags += [ "--sysroot=" +
+                rebase_path("$fuchsia_sdk_path/arch/$target_cpu/sysroot") ]
+    if (target_cpu == "x64") {
+      target_triple = "--target=x86_64-${target_os}"
+    } else if (target_cpu == "arm64") {
+      target_triple = "--target=aarch64-unknown-${target_os}"
+    } else {
+      print("Unknown target CPU for Fuchsia target build.")
+      assert(false)
+    }
+    ldflags += [ target_triple ]
+    cflags += [ target_triple ]
+    asmflags += [ target_triple ]
+  }
+
   if (is_win) {
     if (is_clang && target_cpu == "arm64") {
       cflags += [ "--target=arm64-windows" ]
@@ -64,6 +88,7 @@
       "/bigobj",  # Some of our files are bigger than the regular limits.
       "/utf-8",  # Set Source and Executable character sets to UTF-8.
     ]
+    cflags_cc += [ "/std:c++17" ]
     if (is_clang) {
       cflags += [ "-fms-compatibility-version=19" ]  # 2015
     }
@@ -103,7 +128,7 @@
       cflags += [ "-fPIC" ]
     }
     cflags += [ "-fstrict-aliasing" ]
-    cflags_cc += [ "-std=c++14" ]
+    cflags_cc += [ "-std=c++17" ]
 
     # The main idea is to slim the exported API, but these flags also improve link time on Mac.
     # These would make stack traces worse on Linux, so we don't just set them willy-nilly.
@@ -177,7 +202,10 @@
       "-arch",
       _target,
     ]
-    cflags_cc += [ "-stdlib=libc++" ]
+    cflags_cc += [
+      "-stdlib=libc++",
+      "-fno-aligned-allocation",
+    ]
     ldflags += [
       "-isysroot",
       xcode_sysroot,
@@ -269,6 +297,9 @@
     if (sanitizers == "memory") {
       cflags += [ "-fsanitize-memory-track-origins" ]
     }
+    if (sanitizers == "safe-stack") {
+      cflags_cc += [ "-fno-aligned-allocation" ]
+    }
   }
 }
 
@@ -300,6 +331,9 @@
       # These only show up in shared builds:
       "/wd4251",  # class 'type' needs to have dll-interface to be used by clients of class 'type2'
       "/wd4275",  # non dll-interface class 'base' used as base for dll-interface class 'derived'
+
+      # It'd be nice to fix these and turn this on:
+      "/wd5041",  # out-of-line definition for constexpr static data member is not needed and is deprecated in C++17
     ]
   } else {
     cflags += [
@@ -316,6 +350,17 @@
     cflags_cc += [
       "-Wnon-virtual-dtor",
       "-Wno-noexcept-type",
+
+      # TODO(dogben): The following are new between GCC 6 (stretch) and GCC 8 (buster); they should
+      # all be investigated.
+      "-Wno-array-bounds",
+      "-Wno-class-memaccess",
+      "-Wno-stringop-overflow",
+      "-Wno-restrict",
+      "-Wno-stringop-truncation",
+      "-Wno-sizeof-pointer-memaccess",
+      "-Wno-parentheses",
+      "-Wno-format-truncation",
     ]
   }
 
diff --git a/gn/BUILDCONFIG.gn b/gn/BUILDCONFIG.gn
index 9461e86..6ffe637 100644
--- a/gn/BUILDCONFIG.gn
+++ b/gn/BUILDCONFIG.gn
@@ -78,10 +78,10 @@
   current_cpu = target_cpu
 }
 
-is_clang = is_android || is_ios || is_mac ||
+is_clang = is_android || is_ios || is_mac || is_fuchsia ||
            (cc == "clang" && cxx == "clang++") || clang_win != ""
 if (!is_clang && !is_win) {
-  is_clang = exec_script("gn/is_clang.py",
+  is_clang = exec_script("//gn/is_clang.py",
                          [
                            cc,
                            cxx,
@@ -117,10 +117,6 @@
   }
 }
 
-if (is_mac || is_ios) {
-  ar = "libtool"
-}
-
 if (target_os == "win") {
   # By default we look for 2017 (Enterprise, Pro, and Community), then 2015. If MSVC is installed in a
   # non-default location, you can set win_vc to inform us where it is.
diff --git a/gn/bench.gni b/gn/bench.gni
index cd2ad90..cbd07d9 100644
--- a/gn/bench.gni
+++ b/gn/bench.gni
@@ -22,6 +22,7 @@
   "$_bench/BlurImageFilterBench.cpp",
   "$_bench/BlurRectBench.cpp",
   "$_bench/BlurRectsBench.cpp",
+  "$_bench/BulkRectBench.cpp",
   "$_bench/CTConvertBench.cpp",
   "$_bench/ChartBench.cpp",
   "$_bench/ChecksumBench.cpp",
diff --git a/gn/call.py b/gn/call.py
old mode 100644
new mode 100755
diff --git a/gn/checkdir.py b/gn/checkdir.py
old mode 100644
new mode 100755
diff --git a/gn/checkpath.py b/gn/checkpath.py
new file mode 100755
index 0000000..db8cd98
--- /dev/null
+++ b/gn/checkpath.py
@@ -0,0 +1,9 @@
+#! /usr/bin/env python
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+
+print all(os.path.exists(a) for a in sys.argv[1:])
diff --git a/gn/codesign_ios.py b/gn/codesign_ios.py
old mode 100644
new mode 100755
diff --git a/gn/copy_git_directory.py b/gn/copy_git_directory.py
new file mode 100755
index 0000000..04623ba
--- /dev/null
+++ b/gn/copy_git_directory.py
@@ -0,0 +1,60 @@
+#! /usr/bin/env python
+# Copyright 2019 Google LLC.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import subprocess
+import sys
+
+def copy_git_directory(src, dst, out=None):
+  '''
+  Makes a copy of `src` directory in `dst` directory.  If files already exist
+  and are identical, do not touch them.  If extra files or directories exist,
+  remove them.  Assume that `src` is a git directory so that `git ls-files` can
+  be used to enumerate files.  This has the added benefit of ignoring files
+  not tracked by git.  Also, if out is not None, write summary of actions to out.
+  If `dst` is a top-level git directory, the `.git` directory will be removed.
+  '''
+  if not os.path.isdir(src):
+    raise Exception('Directory "%s" does not exist.' % src)
+  if not os.path.isdir(dst):
+    os.makedirs(dst)
+  ls_files = subprocess.check_output(['git', 'ls-files', '-z', '.'], cwd=src)
+  src_files = set(p for p in ls_files.split('\0') if p)
+  abs_src = os.path.abspath(src)
+  cwd = os.getcwd()
+  try:
+    os.chdir(dst)
+    def output(out, sym, dst, path):
+      if out:
+        out.write('%s %s%s%s\n' % (sym, dst, os.sep, path))
+    for dirpath, dirnames, filenames in os.walk('.', topdown=False):
+      for filename in filenames:
+        path = os.path.normpath(os.path.join(dirpath, filename))
+        if path not in src_files:
+          output(out, '-', dst, path)
+          os.remove(path)
+      for filename in dirnames:
+        path = os.path.normpath(os.path.join(dirpath, filename))
+        if not os.listdir(path):  # Remove empty subfolders.
+          output(out, '-', dst, path + os.sep)
+          os.rmdir(path)
+    for path in src_files:
+      src_path = os.path.join(abs_src, path)
+      if os.path.exists(path):
+        with open(path) as f1:
+          with open(src_path) as f2:
+            if f1.read() == f2.read():
+              continue
+      output(out, '+', dst, path)
+      shutil.copy2(src_path, path)
+  finally:
+    os.chdir(cwd)
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    sys.stderr.write('\nUsage:\n  %s SRC_DIR DST_DIR\n\n' % sys.argv[0])
+    sys.exit(1)
+  copy_git_directory(sys.argv[1], sys.argv[2], sys.stdout)
diff --git a/gn/core.gni b/gn/core.gni
index 7b4a92b..030ca4b 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -203,6 +203,7 @@
   "$_src/core/SkGaussFilter.h",
   "$_src/core/SkFlattenable.cpp",
   "$_src/core/SkFont.cpp",
+  "$_src/core/SkFont_serial.cpp",
   "$_src/core/SkFontLCDConfig.cpp",
   "$_src/core/SkFontMgr.cpp",
   "$_src/core/SkFontDescriptor.cpp",
@@ -241,6 +242,7 @@
   "$_src/core/SkMD5.cpp",
   "$_src/core/SkMD5.h",
   "$_src/core/SkMakeUnique.h",
+  "$_src/core/SkMalloc.cpp",
   "$_src/core/SkMallocPixelRef.cpp",
   "$_src/core/SkMask.cpp",
   "$_src/core/SkMask.h",
@@ -548,17 +550,14 @@
 ]
 
 skia_skpicture_public = [
-  "$_include/core/SkMultiPictureDraw.h",
   "$_include/core/SkPicture.h",
   "$_include/core/SkPictureRecorder.h",
 ]
 
 skia_skpicture_sources = [
-  "$_include/core/SkMultiPictureDraw.h",
   "$_include/core/SkPicture.h",
   "$_include/core/SkPictureRecorder.h",
   "$_src/core/SkBigPicture.cpp",
-  "$_src/core/SkMultiPictureDraw.cpp",
   "$_src/core/SkPicture.cpp",
   "$_src/core/SkPictureCommon.h",
   "$_src/core/SkPictureData.cpp",
diff --git a/gn/cp.py b/gn/cp.py
old mode 100644
new mode 100755
diff --git a/gn/create_sksl_enums.py b/gn/create_sksl_enums.py
old mode 100644
new mode 100755
diff --git a/gn/find_msvc.py b/gn/find_msvc.py
old mode 100644
new mode 100755
diff --git a/gn/find_xcode_sysroot.py b/gn/find_xcode_sysroot.py
old mode 100644
new mode 100755
diff --git a/gn/flutter_defines.gni b/gn/flutter_defines.gni
index a0c6b63..60ee9c9 100644
--- a/gn/flutter_defines.gni
+++ b/gn/flutter_defines.gni
@@ -5,7 +5,6 @@
 flutter_defines = [
   "SK_DISABLE_REDUCE_OPLIST_SPLITTING",
   "SK_LEGACY_SKCODEC_NONE_ENUM",
-  "SK_GL",
 
   # Flutter always wants this https://github.com/flutter/flutter/issues/11402
   "SK_ENABLE_DUMP_GPU",
@@ -14,6 +13,8 @@
   "SK_DISABLE_AAA",
 
   # API staging
+  "SK_SUPPORT_LEGACY_PATH_DIRECTION_ENUM",
+  "SK_SUPPORT_LEGACY_PATH_FILLTYPE_ENUM",
 
   # Flutter doesn't deserialize anything.
   "SK_DISABLE_READBUFFER",
@@ -24,3 +25,7 @@
   "SK_DISABLE_LOWP_RASTER_PIPELINE",
   "SK_FORCE_RASTER_PIPELINE_BLITTER",
 ]
+
+if (!is_fuchsia) {
+  flutter_defines += [ "SK_GL" ]
+}
diff --git a/gn/gen_plist_ios.py b/gn/gen_plist_ios.py
old mode 100644
new mode 100755
diff --git a/gn/gm.gni b/gn/gm.gni
index 308ee6f..8607479 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -25,7 +25,6 @@
   "$_gm/arcto.cpp",
   "$_gm/arithmode.cpp",
   "$_gm/asyncrescaleandread.cpp",
-  "$_gm/atlastext.cpp",
   "$_gm/b_119394958.cpp",
   "$_gm/backdrop.cpp",
   "$_gm/backdrop_imagefilter_croprect.cpp",
@@ -144,6 +143,7 @@
   "$_gm/drrect.cpp",
   "$_gm/drrect_small_inner.cpp",
   "$_gm/dstreadshuffle.cpp",
+  "$_gm/ducky_yuv_blend.cpp",
   "$_gm/emboss.cpp",
   "$_gm/emptypath.cpp",
   "$_gm/encode.cpp",
@@ -245,7 +245,6 @@
   "$_gm/mixercolorfilter.cpp",
   "$_gm/modecolorfilters.cpp",
   "$_gm/morphology.cpp",
-  "$_gm/multipicturedraw.cpp",
   "$_gm/nested.cpp",
   "$_gm/ninepatchstretch.cpp",
   "$_gm/nonclosedpaths.cpp",
@@ -291,7 +290,6 @@
   "$_gm/rasterhandleallocator.cpp",
   "$_gm/readpixels.cpp",
   "$_gm/recordopts.cpp",
-  "$_gm/rectangletexture.cpp",
   "$_gm/rects.cpp",
   "$_gm/repeated_bitmap.cpp",
   "$_gm/resizeimagefilter.cpp",
@@ -326,6 +324,7 @@
   "$_gm/skbug_8955.cpp",
   "$_gm/skbug_9319.cpp",
   "$_gm/skinning.cpp",
+  "$_gm/skvm.cpp",
   "$_gm/smallarc.cpp",
   "$_gm/smallpaths.cpp",
   "$_gm/spritebitmap.cpp",
@@ -382,3 +381,9 @@
   "$_gm/xfermodes3.cpp",
   "$_gm/yuvtorgbeffect.cpp",
 ]
+
+gl_gm_sources = [
+  "$_gm/atlastext.cpp",
+  "$_gm/rectangletexture.cpp",
+]
+
diff --git a/gn/gn_to_bp.py b/gn/gn_to_bp.py
old mode 100644
new mode 100755
index 2443358..e649b4b
--- a/gn/gn_to_bp.py
+++ b/gn/gn_to_bp.py
@@ -318,6 +318,7 @@
     'skia_use_fontconfig':                'false',
     'skia_use_fixed_gamma_text':          'true',
     'skia_include_multiframe_procs':      'false',
+    'skia_libgifcodec_path':              '"third_party/libgifcodec"',
   }
   d['target_os'] = target_os
   if target_os == '"android"':
diff --git a/gn/gn_to_bp_utils.py b/gn/gn_to_bp_utils.py
old mode 100644
new mode 100755
diff --git a/gn/gn_to_cmake.py b/gn/gn_to_cmake.py
old mode 100644
new mode 100755
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 21a1b24..49e3a3f 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -21,13 +21,6 @@
   "$_include/gpu/GrTexture.h",
   "$_include/gpu/GrTypes.h",
 
-  "$_include/gpu/gl/GrGLAssembleInterface.h",
-  "$_include/gpu/gl/GrGLConfig.h",
-  "$_include/gpu/gl/GrGLExtensions.h",
-  "$_include/gpu/gl/GrGLFunctions.h",
-  "$_include/gpu/gl/GrGLInterface.h",
-  "$_include/gpu/gl/GrGLTypes.h",
-
   # Private includes
   "$_include/private/GrContext_Base.h",
   "$_include/private/GrGLTypesPriv.h",
@@ -130,10 +123,10 @@
   "$_src/gpu/GrOpsTask.h",
   "$_src/gpu/GrPaint.cpp",
   "$_src/gpu/GrPaint.h",
-  "$_src/gpu/GrPathRendererChain.cpp",
-  "$_src/gpu/GrPathRendererChain.h",
   "$_src/gpu/GrPathRenderer.cpp",
   "$_src/gpu/GrPathRenderer.h",
+  "$_src/gpu/GrPathRendererChain.cpp",
+  "$_src/gpu/GrPathRendererChain.h",
   "$_src/gpu/GrPipeline.cpp",
   "$_src/gpu/GrPipeline.h",
   "$_src/gpu/GrPrimitiveProcessor.cpp",
@@ -449,58 +442,6 @@
   "$_src/gpu/text/GrTextBlobVertexRegenerator.cpp",
   "$_src/gpu/text/GrTextTarget.h",
 
-  "$_src/gpu/gl/GrGLAssembleGLESInterfaceAutogen.cpp",
-  "$_src/gpu/gl/GrGLAssembleGLInterfaceAutogen.cpp",
-  "$_src/gpu/gl/GrGLAssembleHelpers.cpp",
-  "$_src/gpu/gl/GrGLAssembleInterface.cpp",
-  "$_src/gpu/gl/GrGLAssembleWebGLInterfaceAutogen.cpp",
-  "$_src/gpu/gl/GrGLBuffer.cpp",
-  "$_src/gpu/gl/GrGLBuffer.h",
-  "$_src/gpu/gl/GrGLCaps.cpp",
-  "$_src/gpu/gl/GrGLCaps.h",
-  "$_src/gpu/gl/GrGLContext.cpp",
-  "$_src/gpu/gl/GrGLContext.h",
-  "$_src/gpu/gl/GrGLMakeNativeInterface_none.cpp",
-  "$_src/gpu/gl/GrGLDefines.h",
-  "$_src/gpu/gl/GrGLGLSL.cpp",
-  "$_src/gpu/gl/GrGLGLSL.h",
-  "$_src/gpu/gl/GrGLGpu.cpp",
-  "$_src/gpu/gl/GrGLGpu.h",
-  "$_src/gpu/gl/GrGLGpuProgramCache.cpp",
-  "$_src/gpu/gl/GrGLExtensions.cpp",
-  "$_src/gpu/gl/GrGLInterfaceAutogen.cpp",
-  "$_src/gpu/gl/GrGLOpsRenderPass.cpp",
-  "$_src/gpu/gl/GrGLOpsRenderPass.h",
-  "$_src/gpu/gl/GrGLProgram.cpp",
-  "$_src/gpu/gl/GrGLProgram.h",
-  "$_src/gpu/gl/GrGLProgramDataManager.cpp",
-  "$_src/gpu/gl/GrGLProgramDataManager.h",
-  "$_src/gpu/gl/GrGLRenderTarget.cpp",
-  "$_src/gpu/gl/GrGLRenderTarget.h",
-  "$_src/gpu/gl/GrGLSemaphore.cpp",
-  "$_src/gpu/gl/GrGLSemaphore.h",
-  "$_src/gpu/gl/GrGLStencilAttachment.cpp",
-  "$_src/gpu/gl/GrGLStencilAttachment.h",
-  "$_src/gpu/gl/GrGLTexture.cpp",
-  "$_src/gpu/gl/GrGLTexture.h",
-  "$_src/gpu/gl/GrGLTextureRenderTarget.cpp",
-  "$_src/gpu/gl/GrGLTextureRenderTarget.h",
-  "$_src/gpu/gl/GrGLTypesPriv.cpp",
-  "$_src/gpu/gl/GrGLUtil.cpp",
-  "$_src/gpu/gl/GrGLUtil.h",
-  "$_src/gpu/gl/GrGLUniformHandler.cpp",
-  "$_src/gpu/gl/GrGLUniformHandler.h",
-  "$_src/gpu/gl/GrGLVaryingHandler.cpp",
-  "$_src/gpu/gl/GrGLVaryingHandler.h",
-  "$_src/gpu/gl/GrGLVertexArray.cpp",
-  "$_src/gpu/gl/GrGLVertexArray.h",
-
-  # Files for building GLSL shaders
-  "$_src/gpu/gl/builders/GrGLProgramBuilder.cpp",
-  "$_src/gpu/gl/builders/GrGLProgramBuilder.h",
-  "$_src/gpu/gl/builders/GrGLShaderStringBuilder.cpp",
-  "$_src/gpu/gl/builders/GrGLShaderStringBuilder.h",
-
   # GLSL
   "$_src/gpu/glsl/GrGLSL.cpp",
   "$_src/gpu/glsl/GrGLSL.h",
@@ -559,6 +500,66 @@
   "$_src/image/SkSurface_Gpu.cpp",
 ]
 
+skia_gl_gpu_sources = [
+  "$_include/gpu/gl/GrGLAssembleInterface.h",
+  "$_include/gpu/gl/GrGLConfig.h",
+  "$_include/gpu/gl/GrGLExtensions.h",
+  "$_include/gpu/gl/GrGLFunctions.h",
+  "$_include/gpu/gl/GrGLInterface.h",
+  "$_include/gpu/gl/GrGLTypes.h",
+
+  "$_src/gpu/gl/GrGLAssembleGLESInterfaceAutogen.cpp",
+  "$_src/gpu/gl/GrGLAssembleGLInterfaceAutogen.cpp",
+  "$_src/gpu/gl/GrGLAssembleHelpers.cpp",
+  "$_src/gpu/gl/GrGLAssembleInterface.cpp",
+  "$_src/gpu/gl/GrGLAssembleWebGLInterfaceAutogen.cpp",
+  "$_src/gpu/gl/GrGLBuffer.cpp",
+  "$_src/gpu/gl/GrGLBuffer.h",
+  "$_src/gpu/gl/GrGLCaps.cpp",
+  "$_src/gpu/gl/GrGLCaps.h",
+  "$_src/gpu/gl/GrGLContext.cpp",
+  "$_src/gpu/gl/GrGLContext.h",
+  "$_src/gpu/gl/GrGLDefines.h",
+  "$_src/gpu/gl/GrGLGLSL.cpp",
+  "$_src/gpu/gl/GrGLGLSL.h",
+  "$_src/gpu/gl/GrGLGpu.cpp",
+  "$_src/gpu/gl/GrGLGpu.h",
+  "$_src/gpu/gl/GrGLGpuProgramCache.cpp",
+  "$_src/gpu/gl/GrGLExtensions.cpp",
+  "$_src/gpu/gl/GrGLInterfaceAutogen.cpp",
+  "$_src/gpu/gl/GrGLOpsRenderPass.cpp",
+  "$_src/gpu/gl/GrGLOpsRenderPass.h",
+  "$_src/gpu/gl/GrGLProgram.cpp",
+  "$_src/gpu/gl/GrGLProgram.h",
+  "$_src/gpu/gl/GrGLProgramDataManager.cpp",
+  "$_src/gpu/gl/GrGLProgramDataManager.h",
+  "$_src/gpu/gl/GrGLRenderTarget.cpp",
+  "$_src/gpu/gl/GrGLRenderTarget.h",
+  "$_src/gpu/gl/GrGLSemaphore.cpp",
+  "$_src/gpu/gl/GrGLSemaphore.h",
+  "$_src/gpu/gl/GrGLStencilAttachment.cpp",
+  "$_src/gpu/gl/GrGLStencilAttachment.h",
+  "$_src/gpu/gl/GrGLTexture.cpp",
+  "$_src/gpu/gl/GrGLTexture.h",
+  "$_src/gpu/gl/GrGLTextureRenderTarget.cpp",
+  "$_src/gpu/gl/GrGLTextureRenderTarget.h",
+  "$_src/gpu/gl/GrGLTypesPriv.cpp",
+  "$_src/gpu/gl/GrGLUtil.cpp",
+  "$_src/gpu/gl/GrGLUtil.h",
+  "$_src/gpu/gl/GrGLUniformHandler.cpp",
+  "$_src/gpu/gl/GrGLUniformHandler.h",
+  "$_src/gpu/gl/GrGLVaryingHandler.cpp",
+  "$_src/gpu/gl/GrGLVaryingHandler.h",
+  "$_src/gpu/gl/GrGLVertexArray.cpp",
+  "$_src/gpu/gl/GrGLVertexArray.h",
+
+  # Files for building GLSL shaders
+  "$_src/gpu/gl/builders/GrGLProgramBuilder.cpp",
+  "$_src/gpu/gl/builders/GrGLProgramBuilder.h",
+  "$_src/gpu/gl/builders/GrGLShaderStringBuilder.cpp",
+  "$_src/gpu/gl/builders/GrGLShaderStringBuilder.h",
+]
+
 skia_ccpr_sources = [
   # coverage counting path renderer
   "$_src/gpu/ccpr/GrCCAtlas.cpp",
@@ -607,16 +608,16 @@
 ]
 
 skia_nvpr_sources = [
+  "$_src/gpu/gl/GrGLPath.cpp",
+  "$_src/gpu/gl/GrGLPath.h",
+  "$_src/gpu/gl/GrGLPathRendering.cpp",
+  "$_src/gpu/gl/GrGLPathRendering.h",
   "$_src/gpu/GrPath.cpp",
   "$_src/gpu/GrPath.h",
   "$_src/gpu/GrPathProcessor.cpp",
   "$_src/gpu/GrPathProcessor.h",
   "$_src/gpu/GrPathRendering.cpp",
   "$_src/gpu/GrPathRendering.h",
-  "$_src/gpu/gl/GrGLPath.cpp",
-  "$_src/gpu/gl/GrGLPath.h",
-  "$_src/gpu/gl/GrGLPathRendering.cpp",
-  "$_src/gpu/gl/GrGLPathRendering.h",
   "$_src/gpu/ops/GrDrawPathOp.cpp",
   "$_src/gpu/ops/GrDrawPathOp.h",
   "$_src/gpu/ops/GrStencilAndCoverPathRenderer.cpp",
@@ -628,7 +629,7 @@
 skia_gpu_sources += skia_ccpr_sources
 skia_gpu_sources += skia_nvpr_sources
 
-skia_null_gpu_sources = []
+skia_null_gpu_sources = [ "$_src/gpu/gl/GrGLMakeNativeInterface_none.cpp" ]
 
 skia_vk_sources = [
   "$_include/gpu/vk/GrVkBackendContext.h",
@@ -641,8 +642,6 @@
   "$_src/gpu/vk/GrVkAMDMemoryAllocator.h",
   "$_src/gpu/vk/GrVkBuffer.cpp",
   "$_src/gpu/vk/GrVkBuffer.h",
-  "$_src/gpu/vk/GrVkBufferView.cpp",
-  "$_src/gpu/vk/GrVkBufferView.h",
   "$_src/gpu/vk/GrVkCaps.cpp",
   "$_src/gpu/vk/GrVkCaps.h",
   "$_src/gpu/vk/GrVkCommandBuffer.cpp",
diff --git a/gn/highest_version_dir.py b/gn/highest_version_dir.py
old mode 100644
new mode 100755
diff --git a/gn/skia.gni b/gn/skia.gni
index 398d61f..e81309db 100644
--- a/gn/skia.gni
+++ b/gn/skia.gni
@@ -5,12 +5,8 @@
 if (!defined(is_skia_standalone)) {
   is_skia_standalone = false
 }
-is_skia_dev_build = is_skia_standalone && !is_official_build
 
-if (is_fuchsia) {
-  import("//build/fuchsia/sdk.gni")
-  import("//build/vulkan/config.gni")
-}
+is_skia_dev_build = is_skia_standalone && !is_official_build
 
 declare_args() {
   skia_android_serial = ""
@@ -33,10 +29,12 @@
   skia_generate_workarounds = false
   skia_include_multiframe_procs = false
   skia_lex = false
+  skia_libgifcodec_path = "third_party/externals/libgifcodec"
   skia_pdf_subset_harfbuzz = false  # TODO: set skia_pdf_subset_harfbuzz to skia_use_harfbuzz.
   skia_qt_path = getenv("QT_PATH")
   skia_skqp_global_error_tolerance = 0
   skia_tools_require_resources = false
+  skia_update_fuchsia_sdk = false
   skia_use_angle = false
   skia_use_dawn = false
   skia_use_egl = false
@@ -48,11 +46,12 @@
   skia_use_fonthost_mac = is_mac
   skia_use_freetype = is_android || is_fuchsia || is_linux
   skia_use_harfbuzz = true
+  skia_use_gl = !is_fuchsia
   skia_use_icu = !is_fuchsia && !is_ios
   skia_use_libheif = is_skia_dev_build
   skia_use_libjpeg_turbo = true
   skia_use_libpng = true
-  skia_use_libwebp = !is_fuchsia
+  skia_use_libwebp = true
   skia_use_lua = is_skia_dev_build && !is_ios
   skia_use_metal = false
   skia_use_opencl = false
@@ -62,6 +61,8 @@
   skia_use_xps = true
   skia_use_zlib = true
 
+  skia_vtune_path = ""
+
   if (is_ios) {
     skia_ios_identity = ".*Google.*"
     skia_ios_profile = "Google Development"
@@ -78,7 +79,7 @@
   if (is_android) {
     skia_use_vulkan = defined(ndk_api) && ndk_api >= 24
   } else if (is_fuchsia) {
-    skia_use_vulkan = fuchsia_use_vulkan
+    skia_use_vulkan = true
   } else {
     skia_use_vulkan = defined(skia_moltenvk_path) && skia_moltenvk_path != ""
   }
@@ -93,6 +94,7 @@
   skia_enable_nvpr = !skia_enable_flutter_defines
   skia_enable_spirv_validation = is_skia_dev_build && is_debug && !skia_use_dawn
   skia_use_dng_sdk = !is_fuchsia && skia_use_libjpeg_turbo && skia_use_zlib
+  skia_use_libgifcodec = !skia_use_wuffs
   skia_use_sfntly = skia_use_icu
 }
 
diff --git a/gn/tests.gni b/gn/tests.gni
index 608e8c9..1741549 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -15,7 +15,6 @@
   "$_tests/ApplyGammaTest.cpp",
   "$_tests/ArenaAllocTest.cpp",
   "$_tests/AsADashTest.cpp",
-  "$_tests/BackendAllocationTest.cpp",
   "$_tests/BadIcoTest.cpp",
   "$_tests/BitSetTest.cpp",
   "$_tests/BitmapCopyTest.cpp",
@@ -24,6 +23,7 @@
   "$_tests/BlendTest.cpp",
   "$_tests/BlitMaskClip.cpp",
   "$_tests/BlurTest.cpp",
+  "$_tests/BulkRectTest.cpp",
   "$_tests/CTest.cpp",
   "$_tests/CachedDataTest.cpp",
   "$_tests/CachedDecodingPixelRefTest.cpp",
@@ -65,7 +65,6 @@
   "$_tests/DrawPathTest.cpp",
   "$_tests/DrawTextTest.cpp",
   "$_tests/DynamicHashTest.cpp",
-  "$_tests/EGLImageTest.cpp",
   "$_tests/EmptyPathTest.cpp",
   "$_tests/EncodeTest.cpp",
   "$_tests/EncodedInfoTest.cpp",
@@ -86,6 +85,7 @@
   "$_tests/FontMgrFontConfigTest.cpp",
   "$_tests/FontMgrTest.cpp",
   "$_tests/FontNamesTest.cpp",
+  "$_tests/FontTest.cpp",
   "$_tests/FrontBufferedStreamTest.cpp",
   "$_tests/GeometryTest.cpp",
   "$_tests/GifTest.cpp",
@@ -99,7 +99,6 @@
   "$_tests/GrContextAbandonTest.cpp",
   "$_tests/GrContextFactoryTest.cpp",
   "$_tests/GrFinishedFlushTest.cpp",
-  "$_tests/GrGLExtensionsTest.cpp",
   "$_tests/GrMemoryPoolTest.cpp",
   "$_tests/GrMeshTest.cpp",
   "$_tests/GrMipMappedTest.cpp",
@@ -194,7 +193,6 @@
   "$_tests/PromiseImageTest.cpp",
   "$_tests/ProxyConversionTest.cpp",
   "$_tests/ProxyRefTest.cpp",
-  "$_tests/ProxyTest.cpp",
   "$_tests/QuickRejectTest.cpp",
   "$_tests/RRectInPathTest.cpp",
   "$_tests/RTreeTest.cpp",
@@ -210,13 +208,11 @@
   "$_tests/RecorderTest.cpp",
   "$_tests/RecordingXfermodeTest.cpp",
   "$_tests/RectTest.cpp",
-  "$_tests/RectangleTextureTest.cpp",
   "$_tests/RefCntTest.cpp",
   "$_tests/RegionTest.cpp",
   "$_tests/RenderTargetContextTest.cpp",
   "$_tests/RepeatedClippedBlurTest.cpp",
   "$_tests/ResourceAllocatorTest.cpp",
-  "$_tests/ResourceCacheTest.cpp",
   "$_tests/RoundRectTest.cpp",
   "$_tests/SRGBReadWritePixelsTest.cpp",
   "$_tests/SRGBTest.cpp",
@@ -271,7 +267,6 @@
   "$_tests/StrokerTest.cpp",
   "$_tests/SubsetPath.cpp",
   "$_tests/SubsetPath.h",
-  "$_tests/SurfaceSemaphoreTest.cpp",
   "$_tests/SurfaceTest.cpp",
   "$_tests/SwizzlerTest.cpp",
   "$_tests/TArrayTest.cpp",
@@ -286,12 +281,10 @@
   "$_tests/TestUtils.h",
   "$_tests/TextBlobCacheTest.cpp",
   "$_tests/TextBlobTest.cpp",
-  "$_tests/TextureBindingsResetTest.cpp",
   "$_tests/TextureProxyTest.cpp",
   "$_tests/TextureStripAtlasManagerTest.cpp",
   "$_tests/Time.cpp",
   "$_tests/TopoSortTest.cpp",
-  "$_tests/TraceMemoryDumpTest.cpp",
   "$_tests/TracingTest.cpp",
   "$_tests/TransferPixelsTest.cpp",
   "$_tests/TypefaceTest.cpp",
@@ -313,7 +306,22 @@
   "$_tests/YUVTest.cpp",
 ]
 
-metal_tests_sources = [ "$_tests/MtlBackendAllocationTest.mm" ]
+gl_tests_sources = [
+  "$_tests/BackendAllocationTest.cpp",
+  "$_tests/EGLImageTest.cpp",
+  "$_tests/GrGLExtensionsTest.cpp",
+  "$_tests/ProxyTest.cpp",
+  "$_tests/RectangleTextureTest.cpp",
+  "$_tests/ResourceCacheTest.cpp",
+  "$_tests/SurfaceSemaphoreTest.cpp",
+  "$_tests/TextureBindingsResetTest.cpp",
+  "$_tests/TraceMemoryDumpTest.cpp",
+]
+
+metal_tests_sources = [
+  "$_tests/MtlBackendAllocationTest.mm",
+  "$_tests/MtlCopySurfaceTest.mm",
+]
 
 pathops_tests_sources = [
   "$_tests/PathOpsAngleIdeas.cpp",
diff --git a/gn/toolchain/BUILD.gn b/gn/toolchain/BUILD.gn
index 96e3176..55e33e2 100644
--- a/gn/toolchain/BUILD.gn
+++ b/gn/toolchain/BUILD.gn
@@ -1,3 +1,7 @@
+if (is_fuchsia) {
+  import("//build/fuchsia/sdk.gni")
+}
+
 declare_args() {
   host_ar = ar
   host_cc = cc
@@ -15,6 +19,13 @@
       target_cc = "$_prefix/$ndk_target$ndk_api-clang"
       target_cxx = "$_prefix/$ndk_target$ndk_api-clang++"
     }
+  } else if (is_fuchsia && using_fuchsia_sdk) {
+    target_ar = rebase_path("$fuchsia_toolchain_path/bin/llvm-ar")
+    target_cc = rebase_path("$fuchsia_toolchain_path/bin/clang")
+    target_cxx = rebase_path("$fuchsia_toolchain_path/bin/clang++")
+    cflags = "--sysroot=" +
+             rebase_path("$fuchsia_toolchain_path/$target_cpu/sysroot")
+    link = rebase_path("$fuchsia_toolchain_path/bin/ld.lld")
   } else {
     target_ar = ar
     target_cc = cc
@@ -254,10 +265,13 @@
       description = "assemble {{source}}"
     }
 
+    if (is_mac || is_ios) {
+      not_needed([ "ar" ])  # We use libtool instead.
+    }
+
     tool("alink") {
       if (is_mac || is_ios) {
-        command =
-            "$ar -static -o {{output}} -no_warning_for_no_symbols {{inputs}}"
+        command = "libtool -static -o {{output}} -no_warning_for_no_symbols {{inputs}}"
       } else {
         rspfile = "{{output}}.rsp"
         rspfile_content = "{{inputs}}"
@@ -291,7 +305,7 @@
       # LLD doesn't need these flags, but accepts and ignores them.
       _start_group = "-Wl,--start-group"
       _end_group = "-Wl,--end-group"
-      if (is_mac || is_ios) {
+      if (is_mac || is_ios || is_fuchsia) {
         _start_group = ""
         _end_group = ""
       }
@@ -318,7 +332,7 @@
       # LLD doesn't need these flags, but accepts and ignores them.
       _start_group = "-Wl,--start-group"
       _end_group = "-Wl,--end-group"
-      if (is_mac || is_ios) {
+      if (is_mac || is_ios || is_fuchsia) {
         _start_group = ""
         _end_group = ""
       }
diff --git a/gn/toolchain/num_cpus.py b/gn/toolchain/num_cpus.py
old mode 100644
new mode 100755
diff --git a/go.mod b/go.mod
index 51ce597..65d20c1 100644
--- a/go.mod
+++ b/go.mod
@@ -3,71 +3,11 @@
 go 1.12
 
 require (
-	cloud.google.com/go v0.47.0 // indirect
-	cloud.google.com/go/bigquery v1.1.0 // indirect
 	cloud.google.com/go/storage v1.1.2
-	github.com/Azure/go-autorest v13.2.0+incompatible // indirect
-	github.com/NYTimes/gziphandler v1.1.1 // indirect
-	github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
-	github.com/aws/aws-sdk-go v1.25.18 // indirect
-	github.com/coreos/bbolt v1.3.3 // indirect
-	github.com/coreos/etcd v3.3.17+incompatible // indirect
-	github.com/coreos/go-semver v0.3.0 // indirect
-	github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
-	github.com/creack/pty v1.1.9 // indirect
-	github.com/danjacques/gofslock v0.0.0-20191023191349-0a45f885bc37 // indirect
-	github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
-	github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect
-	github.com/elazarl/goproxy/ext v0.0.0-20191011121108-aa519ddbe484 // indirect
-	github.com/emicklei/go-restful v2.11.0+incompatible // indirect
-	github.com/evanphx/json-patch v4.5.0+incompatible // indirect
 	github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633
-	github.com/fortytw2/leaktest v1.3.0 // indirect
-	github.com/go-openapi/jsonreference v0.19.3 // indirect
-	github.com/go-openapi/spec v0.19.4 // indirect
-	github.com/gogo/protobuf v1.3.1 // indirect
 	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
-	github.com/google/addlicense v0.0.0-20190907113143-be125746c2c4 // indirect
-	github.com/google/pprof v0.0.0-20191022163618-5260658b92d7 // indirect
 	github.com/google/uuid v1.1.1
-	github.com/gophercloud/gophercloud v0.6.0 // indirect
-	github.com/gorilla/websocket v1.4.1 // indirect
-	github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
-	github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect
-	github.com/grpc-ecosystem/grpc-gateway v1.11.3 // indirect
-	github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
-	github.com/kr/pty v1.1.8 // indirect
-	github.com/magiconair/properties v1.8.1 // indirect
-	github.com/mailru/easyjson v0.7.0 // indirect
-	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
-	github.com/pelletier/go-toml v1.5.0 // indirect
-	github.com/prometheus/client_golang v1.2.1 // indirect
-	github.com/rogpeppe/fastuuid v1.2.0 // indirect
-	github.com/rogpeppe/go-charset v0.0.0-20190617161244-0dc95cdf6f31 // indirect
-	github.com/rogpeppe/go-internal v1.5.0 // indirect
-	github.com/russross/blackfriday v2.0.0+incompatible // indirect
-	github.com/spf13/jwalterweatherman v1.1.0 // indirect
-	github.com/spf13/viper v1.4.0 // indirect
-	github.com/ugorji/go v1.1.7 // indirect
-	go.chromium.org/luci v0.0.0-20191024015310-9771d487fd3c // indirect
-	go.etcd.io/bbolt v1.3.3 // indirect
-	go.skia.org/infra v0.0.0-20191024032605-d60543c65849
-	go.uber.org/multierr v1.2.0 // indirect
-	go.uber.org/zap v1.11.0 // indirect
-	golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
-	golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
-	golang.org/x/mobile v0.0.0-20191002175909-6d0d39b2ca82 // indirect
-	golang.org/x/net v0.0.0-20191021144547-ec77196f6094 // indirect
-	golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2 // indirect
-	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
-	golang.org/x/tools v0.0.0-20191024044240-7b6f5d95f3a9 // indirect
-	golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
+	go.chromium.org/luci v0.0.0-20191119055648-478869e8bc76 // indirect
+	go.skia.org/infra v0.0.0-20191118172813-369d64620dd4
 	google.golang.org/api v0.11.0
-	google.golang.org/appengine v1.6.5 // indirect
-	k8s.io/api v0.0.0-20191024025707-4cb0a757333c // indirect
-	k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f // indirect
-	k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d // indirect
-	k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 // indirect
-	sigs.k8s.io/structured-merge-diff v0.0.0-20191023203907-336d3378ca53 // indirect
 )
diff --git a/go.sum b/go.sum
index 0933f22..8563615 100644
--- a/go.sum
+++ b/go.sum
@@ -14,11 +14,8 @@
 cloud.google.com/go v0.46.1/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
 cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.47.0 h1:1JUtpcY9E7+eTospEwWS2QXP3DEn7poB3E2j0jN74mM=
-cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU=
 cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.1.0/go.mod h1:g4RsfUkOvV3Vi5yRujQETpqwCN0F+faPZ2/ykNYfBJc=
 cloud.google.com/go/bigtable v1.0.0 h1:2DCxzxiuoWubL6J0yl4rUtFtIJAX566mcefQS3xy6us=
 cloud.google.com/go/bigtable v1.0.0/go.mod h1:N+NeT8ICfOM1Ek4CeP03mSESb2x+qLkSC0+CRBEsvAA=
 cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
@@ -28,19 +25,14 @@
 cloud.google.com/go/logging v1.0.0/go.mod h1:V1cc3ogwobYzQq5f2R7DS/GvRIrI4FKj01Gs5glwAls=
 cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 cloud.google.com/go/storage v1.1.0 h1:KYV0dnmEcOuxTd8YLiuQqfx8PzSwDeuDvYGoa5+DbDI=
 cloud.google.com/go/storage v1.1.0/go.mod h1:a81gKs1KmeOyF/qrbeu4APVXICPLcsl0Ilx2XvD7ZYU=
-cloud.google.com/go/storage v1.1.1 h1:ycCxVkVbeNQj8t43giBuzCUJb9g5j1QHua8es8DMb/E=
-cloud.google.com/go/storage v1.1.1/go.mod h1:nbQkUX8zrWh07WKekXr/Phd0q/ERj4IOJnkE+v56Qys=
 cloud.google.com/go/storage v1.1.2 h1:q7KNypEb3CARnitCAqY63g+dZp9HDEgv/c6IPlPLMJI=
 cloud.google.com/go/storage v1.1.2/go.mod h1:/03MkR5FWjF0OpcKpdJ4RgWybEaYAr2boHXq5RDlxbw=
 contrib.go.opencensus.io/exporter/stackdriver v0.12.7/go.mod h1:ZOhmSfHIoyVaQ+bKN+lR4h7K2olTIJsrdOwWHsNGw4w=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/99designs/goodies v0.0.0-20140916053233-ec7f410f2ff2/go.mod h1:cbC1BMQYOqZ70I8LEvH53q53MzD33COOamPrxo0Y3Wk=
 github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest v13.0.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest v13.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@@ -49,29 +41,17 @@
 github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
 github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/VividCortex/godaemon v0.0.0-20150910212227-3d9f6e0b234f/go.mod h1:Y8CJ3IwPIAkMhv/rRUWIlczaeqd9ty9yrl+nc2AbaL4=
 github.com/a8m/envsubst v1.1.0/go.mod h1:91m2Q6AZE0w4WD/laQam2MtWq6FxJVm7UqcB30DeYxw=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 github.com/aws/aws-sdk-go v1.22.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.25.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.15/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.16/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.17/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.25.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -82,60 +62,34 @@
 github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA=
-github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/danjacques/gofslock v0.0.0-20180405201223-afa47669cc54 h1:pG2ZY7syxD1TwveleQBnIq58yZE1NldHkjs8N1eHBl8=
 github.com/danjacques/gofslock v0.0.0-20180405201223-afa47669cc54/go.mod h1:DC3JtzuG7kxMvJ6dZmf2ymjNyoXwgtklr7FN+Um2B0U=
-github.com/danjacques/gofslock v0.0.0-20191023191349-0a45f885bc37/go.mod h1:DC3JtzuG7kxMvJ6dZmf2ymjNyoXwgtklr7FN+Um2B0U=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
-github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
-github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
-github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
-github.com/elazarl/goproxy/ext v0.0.0-20191011121108-aa519ddbe484/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.10.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.11.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fiorix/go-web v1.0.1-0.20150221144011-5b593f1e8966 h1:P/Czr+qFBdKELw4nys0x2e5nkT9niVq/2FS63ArJzm4=
 github.com/fiorix/go-web v1.0.1-0.20150221144011-5b593f1e8966/go.mod h1:5OPf/2cFhfql2NdV8pCcv9fZJ0e0LC//L+72iX1cqDM=
 github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633 h1:xJMmr4GMYIbALX5edyoDIOQpc2bOQTeJiWMeCl9lX/8=
 github.com/flynn/json5 v0.0.0-20160717195620-7620272ed633/go.mod h1:NJDK3/o7abx6PP54EOe0G0n0RLmhCo9xv61gUYpI0EY=
 github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -143,29 +97,18 @@
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
 github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc h1:55rEp52jU6bkyslZ1+C/7NGfpQsEc6pxGLAGDOctqbw=
@@ -184,7 +127,6 @@
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/addlicense v0.0.0-20190510175307-22550fa7c1b0/go.mod h1:QtPG26W17m+OIQgE6gQ24gC1M6pUaMBAbFrTIDtwG/E=
-github.com/google/addlicense v0.0.0-20190907113143-be125746c2c4/go.mod h1:QtPG26W17m+OIQgE6gQ24gC1M6pUaMBAbFrTIDtwG/E=
 github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -203,9 +145,6 @@
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190930153522-6ce02741cba3/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191016141926-35da2c455f8f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191022163618-5260658b92d7/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
@@ -217,9 +156,6 @@
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
 github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
-github.com/gophercloud/gophercloud v0.4.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gophercloud/gophercloud v0.5.0/go.mod h1:b1k/BkBA9smzYde6p6zYLIe5JNAEoJzUWaIRh+9A/j0=
-github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f h1:TyqzGm2z1h3AGhjOoRYyeLcW4WlW81MDQkWa+rx/000=
 github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -227,15 +163,7 @@
 github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.11.3/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
@@ -245,6 +173,7 @@
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
 github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
@@ -257,7 +186,6 @@
 github.com/jcgregorio/slog v0.0.0-20190423190439-e6f2d537f900 h1:H8hiPQr5PtkrB5z3Do/9iR5tEwuAFNim68cqcoAlHeY=
 github.com/jcgregorio/slog v0.0.0-20190423190439-e6f2d537f900/go.mod h1:YT3uVwwZ2P4vmZcM3xICUNJ6dqBwoiSgVAqxHu3rcoo=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -269,36 +197,30 @@
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/luci/gtreap v0.0.0-20161228054646-35df89791e8f/go.mod h1:OjKOY0UvVOOH5nWXSIWTbQWESn8dDiGlaEZx6IAsWhU=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 github.com/maruel/subcommands v0.0.0-20181220013616-967e945be48b/go.mod h1:4cd1CVd4c9phb1z9fTkV+JbmnFm394Hp9rHEAOvD+vs=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@@ -308,13 +230,9 @@
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20190414153302-2ae31c8b6b30/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -324,11 +242,10 @@
 github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -336,22 +253,15 @@
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
 github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_golang v1.2.0 h1:g4yo/h/me4ZL9o0SVHNRdS2jn5SY8GDmMgkhQ8Mz70s=
-github.com/prometheus/client_golang v1.2.0/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
-github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI=
-github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
@@ -359,26 +269,16 @@
 github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
 github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
 github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
 github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4=
 github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d h1:1VUlQbCfkoSGv7qP7Y+ro3ap1P1pPZxgdGVqiTVy5C4=
 github.com/robertkrimen/otto v0.0.0-20180617131154-15f95af6e78d/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
-github.com/rogpeppe/go-charset v0.0.0-20190617161244-0dc95cdf6f31/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -390,21 +290,21 @@
 github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
@@ -418,95 +318,48 @@
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/texttheater/golang-levenshtein v0.0.0-20190717060638-b7aaf30637d6 h1:aROOQJhj3zNd21TC06HYxLSlBTr4pyNkcvfQ/ITSGQA=
 github.com/texttheater/golang-levenshtein v0.0.0-20190717060638-b7aaf30637d6/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
 github.com/unrolled/secure v1.0.4/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc=
 github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5 h1:Xim2mBRFdXzXmKRO8DJg/FJtn/8Fj9NOEpO6+WuMPmk=
 github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5/go.mod h1:ppEjwdhyy7Y31EnHRDm1JkChoC7LXIJ7Ex0VYLWtZtQ=
 github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
 github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 github.com/yosuke-furukawa/json5 v0.1.1/go.mod h1:sw49aWDqNdRJ6DYUtIQiaA3xyj2IL9tjeNYmX2ixwcU=
 github.com/zeebo/bencode v1.0.0 h1:zgop0Wu1nu4IexAZeCZ5qbsjU4O1vMrfCrVgUjbHVuA=
 github.com/zeebo/bencode v1.0.0/go.mod h1:Ct7CkrWIQuLWAy9M3atFHYq4kG9Ao/SsY5cdtCXmp9Y=
 go.chromium.org/gae v0.0.0-20190826183307-50a499513efa/go.mod h1:ypuIZj/TmtaQgUYPNNu0iKlsUkuv10PROeqHCNrqrog=
-go.chromium.org/luci v0.0.0-20191005073036-345b0aec4e74 h1:hTWXfGE68FvtceDaMint5PqRN0vAbOmGxeMJj7SzIEc=
-go.chromium.org/luci v0.0.0-20191005073036-345b0aec4e74/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191007184019-f6f465957ef4 h1:p9wULFOdgnlBqSKXnSylM/fFCzwQg5o2q7M8cH/iPIY=
-go.chromium.org/luci v0.0.0-20191007184019-f6f465957ef4/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191008012701-86f88e50331b h1:0FgAXZ0LGuF0PGkevDQT802pgvipg0p4dFj/Qnv2AQg=
-go.chromium.org/luci v0.0.0-20191008012701-86f88e50331b/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
+go.chromium.org/luci v0.0.0-20191015220223-dd3644d64ff9 h1:tOzif5IkoxTlP+wYE08zK/ZWucKBt1/tJu6khA6hDGM=
 go.chromium.org/luci v0.0.0-20191015220223-dd3644d64ff9/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191017030413-7e44e8050726 h1:LbUTNTv7eM7trUfPz2O7WNpOfFOigQqlx1+Bi2Amuq8=
-go.chromium.org/luci v0.0.0-20191017030413-7e44e8050726/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191017232650-fdd3869c268f h1:XaSxemmgKm2CpBXz28FGlhuR/R5rOvHdUGSxjo2oam4=
-go.chromium.org/luci v0.0.0-20191017232650-fdd3869c268f/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191018230245-0b73213425c2 h1:kilGW1xuBiLmdRidUtJ0XPlMh3/eAGU/sg2PW+qCxdY=
-go.chromium.org/luci v0.0.0-20191018230245-0b73213425c2/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191022033439-ac13248965e3 h1:mM803Sqoki0LQ/hwySr2i1AUpnwDD3/Jxs9cEfBnUV8=
-go.chromium.org/luci v0.0.0-20191022033439-ac13248965e3/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191023045027-570c82c2f891 h1:psUyVcj7tc1u33tJFQnONuZQj2Ak8fv48pwbvhZh2E8=
-go.chromium.org/luci v0.0.0-20191023045027-570c82c2f891/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.chromium.org/luci v0.0.0-20191024015310-9771d487fd3c h1:Ao1DR+xAZOE3bvdYytWQGkFGCg12zknII9ZOrRZ32M8=
-go.chromium.org/luci v0.0.0-20191024015310-9771d487fd3c/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.chromium.org/luci v0.0.0-20191119055648-478869e8bc76 h1:Do8VBFnsvSJDRBHyPuq0N3mp3u6tooMzq5Jpe63rpw4=
+go.chromium.org/luci v0.0.0-20191119055648-478869e8bc76/go.mod h1:MIQewVTLvOvc0UioV0JNqTNO/RspKFS0XEeoKrOxsdM=
 go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.opencensus.io v0.22.1 h1:8dP3SGL7MPB94crU3bEPplMPe83FI4EouesJUeFHv50=
 go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
-go.skia.org/infra v0.0.0-20191007190028-3345eb620d11 h1:Pp6zcJKMpyMEe5rCivc8fvwLfvmzZqn/FgYP/ZTMLD4=
-go.skia.org/infra v0.0.0-20191007190028-3345eb620d11/go.mod h1:lipauW+yX2lI3nWVtNCE5pJVTwPUAiys4VYmbjGwrzo=
-go.skia.org/infra v0.0.0-20191008021319-bbb1d2b20bc8 h1:xP+Z6L7SQrD7MpHOlIFZgP6jTN2TeHOS5ZTLtCMksms=
-go.skia.org/infra v0.0.0-20191008021319-bbb1d2b20bc8/go.mod h1:lipauW+yX2lI3nWVtNCE5pJVTwPUAiys4VYmbjGwrzo=
-go.skia.org/infra v0.0.0-20191017021031-fc01b2d62386 h1:z5VXNDYh7+ty/gMURRfbs8ARVMAOr28qbYUt0XV6anw=
-go.skia.org/infra v0.0.0-20191017021031-fc01b2d62386/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191018012907-e925b7218e0c h1:XSEQJ+UKHvU/PpAyaW7rXfuSQB86DHWp/3jW8oTppGc=
-go.skia.org/infra v0.0.0-20191018012907-e925b7218e0c/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191018201008-a87be9a88384 h1:soP/RpsG7Th6yvxewVmIqhgfbFW+6Ukm/nzQUOTmpiI=
-go.skia.org/infra v0.0.0-20191018201008-a87be9a88384/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191019202855-3ee592e07fc4 h1:N33ks9aQKYTaxFeTEVpq2IpXzW4gGLsOwYas0HiOgxY=
-go.skia.org/infra v0.0.0-20191019202855-3ee592e07fc4/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191021231233-1b79b8492ad2 h1:jGwsZtpWbuqUBmAcxhLMWkyHV6njv7IZ2Xvw3Bq7FOQ=
-go.skia.org/infra v0.0.0-20191021231233-1b79b8492ad2/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191023040424-51844ddbe0c6 h1:dGPzcBQTArQDOCOlsEkvDjNU0vSnLYlNVrKNMVO/mKc=
-go.skia.org/infra v0.0.0-20191023040424-51844ddbe0c6/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.skia.org/infra v0.0.0-20191024032605-d60543c65849 h1:8GEWTCAw8ohXNC1RTMx04uQMajqD1pv1E3Yi/bSVVKI=
-go.skia.org/infra v0.0.0-20191024032605-d60543c65849/go.mod h1:i1Rr16s/HPagvyT4LYfrgZ4/9kf1NQ+paaDyDOXsZcA=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.11.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.skia.org/infra v0.0.0-20191118172813-369d64620dd4 h1:HYGJDLo9khjz74w0/TL7ZbfHM8g+6VYHsC83vbPNHOc=
+go.skia.org/infra v0.0.0-20191118172813-369d64620dd4/go.mod h1:JDrWsoT8J2u38m70kr1/K7W7OVaDX9KbMGlDMKSL2sM=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
 golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20190912063710-ac5d2bfcbfe0/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs=
 golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e h1:ewBcnrlKhy0GKnQ31tXkOC/G7/jHC4ogar1TiIfANC4=
 golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -517,7 +370,6 @@
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mobile v0.0.0-20191002175909-6d0d39b2ca82/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -525,7 +377,6 @@
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -534,7 +385,6 @@
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYRuq8JQ1aa7LJt8EXVyo=
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
@@ -542,15 +392,8 @@
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191003171128-d98b1b443823 h1:Ypyv6BNJh07T1pUSrehkLemqPKXhus2MkfktJ91kRh4=
 golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191007182048-72f939374954 h1:JGZucVF/L/TotR719NbujzadOZ2AgnYlqphQGHDCKaU=
-golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
-golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8=
-golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
@@ -569,10 +412,8 @@
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -589,18 +430,6 @@
 golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191007092633-5f54ce542709 h1:wGFnQAzp3osDbpU/aeSzlZ7/hHjXuMgcAX3p8UKPEcA=
 golang.org/x/sys v0.0.0-20191007092633-5f54ce542709/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191007154456-ef33b2fb2c41 h1:OC2BiV9nQHWgVMNbxZ5/eZKWnnd3Z4H9W5zdNvC4EBc=
-golang.org/x/sys v0.0.0-20191007154456-ef33b2fb2c41/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ=
-golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA=
-golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2 h1:I7efaDQAsIQmkTF+WSdcydwVWzK07Yuz8IFF8rNkDe0=
-golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -611,8 +440,6 @@
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -627,38 +454,20 @@
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac h1:MQEvx39qSf8vyrx3XRaOe+j1UDIzKwkYOVObRgGPVqI=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479 h1:lfN2PY/jymfnxkNHlbBF5DwPsUvhqUnrdgfK01iH2s0=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190917162342-3b4f30a44f3b/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e h1:1xWUkZQQ9Z9UuZgNaIR6OQOE7rUFglXUUBZlO+dGg6I=
 golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191005014404-c9f9432ec4b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191007185444-6536af71d98a h1:mtF1GhqcFEC1RVSQxvgrZWOM22dax6fiM9VfcQoTv6U=
-golang.org/x/tools v0.0.0-20191007185444-6536af71d98a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191017035025-0abb09c987dd h1:05UlZ/D8j3ueatR0iMtGQ8BOUKChM5b0XY/c50tSFe4=
-golang.org/x/tools v0.0.0-20191017035025-0abb09c987dd/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191018000036-341939e08647 h1:kfteFQIbFl9R5RwgjPgqDtpdeSnP/IfGo1R0ngom5vs=
-golang.org/x/tools v0.0.0-20191018000036-341939e08647/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a h1:UuQ+70Pi/ZdWHuP4v457pkXeOynTdgd/4enxeIO/98k=
-golang.org/x/tools v0.0.0-20191018212557-ed542cd5b28a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191021224128-7178990c2503 h1:p5I5qqdhgoR6HIkb5DERflOgVfSkE/BjgCc5AdjWpWE=
-golang.org/x/tools v0.0.0-20191021224128-7178990c2503/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191022210528-83d82311fd1f h1:X4UYO3m0+b0v4ctMUiMVB/vdVP5v25QRYMtH88N+Ne8=
 golang.org/x/tools v0.0.0-20191022210528-83d82311fd1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191022213345-0bbdf54effa2 h1:PD/5/WuAFfp8HtzZpgrO61i5F/PE9IBhBJCQcJ4igdA=
-golang.org/x/tools v0.0.0-20191022213345-0bbdf54effa2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191024044240-7b6f5d95f3a9 h1:DO63+qvNhQJJdfC5kGf38QzyUHcy1abiLauHXrsabMI=
-golang.org/x/tools v0.0.0-20191024044240-7b6f5d95f3a9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
@@ -677,7 +486,6 @@
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
 google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -696,16 +504,11 @@
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20191002211648-c459b9ce5143 h1:tikhlQEJeezbnu0Zcblj7g5vm/L7xt6g1vnfq8mRCS4=
 google.golang.org/genproto v0.0.0-20191002211648-c459b9ce5143/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191007162740-aa923e3a3354 h1:KJxw2DvYTCIxlEY4yqWyLdvFGlci4EKTCbrZwfyxDME=
-google.golang.org/genproto v0.0.0-20191007162740-aa923e3a3354/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd h1:84VQPzup3IpKLxuIAZjHMhVjJ8fZ4/i3yUnj3k6fUdw=
-google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8=
 google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
@@ -722,11 +525,9 @@
 gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/olivere/elastic.v5 v5.0.82/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
 gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -740,33 +541,19 @@
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 k8s.io/api v0.0.0-20190425012535-181e1f9c52c1/go.mod h1:AhUc3Ph6fhRc0SCpt0Hwv0E+Q8QiEAASkXKwfmT2JwU=
 k8s.io/api v0.0.0-20191005115622-2e41325d9e4b/go.mod h1:V9fqJJO3eGaWUKb9e6wH3fx7JXl1IaSC1VhSLk7GJjA=
-k8s.io/api v0.0.0-20191016225839-816a9b7df678/go.mod h1:LZQaT8MvVpl7Bg2lYFcQm7+Mpdxq8p1NFl3yh+5DCwY=
-k8s.io/api v0.0.0-20191024025707-4cb0a757333c/go.mod h1:wThzAkkfqqxz0ERi8QQEOwQfkBFM27QYtPxuvBI9p+0=
 k8s.io/apimachinery v0.0.0-20190424212440-527a9d33701e/go.mod h1:5CBnzrKYGHzv9ZsSKmQ8wHt4XI4/TUBPDwYM9FlZMyw=
 k8s.io/apimachinery v0.0.0-20190425132440-17f84483f500/go.mod h1:5CBnzrKYGHzv9ZsSKmQ8wHt4XI4/TUBPDwYM9FlZMyw=
 k8s.io/apimachinery v0.0.0-20191005115455-e71eb83a557c/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
 k8s.io/apimachinery v0.0.0-20191006235458-f9f2f3f8ab02/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
-k8s.io/apimachinery v0.0.0-20191016225534-b1267f8c42b4/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
-k8s.io/apimachinery v0.0.0-20191017185446-6e68a40eebf9/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
-k8s.io/apimachinery v0.0.0-20191020214737-6c8691705fc5/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
-k8s.io/apimachinery v0.0.0-20191024025529-62ce3d1e6a82/go.mod h1:92mWDd8Ji2sw2157KIgino5wCxffA8KSvhW2oY4ypdw=
 k8s.io/client-go v0.0.0-20190425172711-65184652c889/go.mod h1:PeVFCnjeDy6EwLN+wdDIZd1DwDY6jnkpQt9psMo5YRU=
 k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20190907103519-ebc107f98eab/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20191010091904-7fa3014cb28f/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
 k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
-k8s.io/kube-openapi v0.0.0-20190918143330-0270cf2f1c1d/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
 k8s.io/utils v0.0.0-20190923111123-69764acb6e8e/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
-sigs.k8s.io/structured-merge-diff v0.0.0-20191007172110-13ba6db87b8b/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
-sigs.k8s.io/structured-merge-diff v0.0.0-20191009170950-ae447d53f5c3/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
-sigs.k8s.io/structured-merge-diff v0.0.0-20191022230812-e660f95f9d84/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
-sigs.k8s.io/structured-merge-diff v0.0.0-20191023203907-336d3378ca53/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/include/android/SkAnimatedImage.h b/include/android/SkAnimatedImage.h
index 760dabd..a3907a6 100644
--- a/include/android/SkAnimatedImage.h
+++ b/include/android/SkAnimatedImage.h
@@ -110,6 +110,16 @@
         return fRepetitionCount;
     }
 
+    /**
+     *  Return the total number of frames in the animation.
+     */
+    int getFrameCount() const { return fFrameCount; }
+
+    /**
+     * Return the (possibly scaled) dimensions of the image.
+     */
+    SkISize dimensions() const { return fScaledSize; }
+
 protected:
     SkRect onGetBounds() override;
     void onDraw(SkCanvas*) override;
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 7960eb5..f168967 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -21,7 +21,6 @@
 class SkPaint;
 class SkPixelRef;
 class SkShader;
-class SkString;
 
 /** \class SkBitmap
     SkBitmap describes a two-dimensional raster pixel array. SkBitmap is built on
diff --git a/include/core/SkBlendMode.h b/include/core/SkBlendMode.h
index 270978f..1dff8a7 100644
--- a/include/core/SkBlendMode.h
+++ b/include/core/SkBlendMode.h
@@ -45,6 +45,34 @@
     kLastMode          = kLuminosity, //!< last valid value
 };
 
+/**
+ * For Porter-Duff SkBlendModes (those <= kLastCoeffMode), these coefficients describe the blend
+ * equation used. Coefficient-based blend modes specify an equation:
+ * ('dstCoeff' * dst + 'srcCoeff' * src), where the coefficient values are constants, functions of
+ * the src or dst alpha, or functions of the src or dst color.
+ */
+enum class SkBlendModeCoeff {
+    kZero, /** 0 */
+    kOne,  /** 1 */
+    kSC,   /** src color */
+    kISC,  /** inverse src color (i.e. 1 - sc) */
+    kDC,   /** dst color */
+    kIDC,  /** inverse dst color (i.e. 1 - dc) */
+    kSA,   /** src alpha */
+    kISA,  /** inverse src alpha (i.e. 1 - sa) */
+    kDA,   /** dst alpha */
+    kIDA,  /** inverse dst alpha (i.e. 1 - da) */
+
+    kCoeffCount
+};
+
+/**
+ * Returns true if 'mode' is a coefficient-based blend mode (<= kLastCoeffMode). If true is
+ * returned, the mode's src and dst coefficient functions are set in 'src' and 'dst'.
+ */
+SK_API bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst);
+
+
 /** Returns name of blendMode as null-terminated C string.
 
     @param blendMode  one of:
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 2237337..373b779 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -286,6 +286,11 @@
     */
     virtual GrContext* getGrContext();
 
+    /** Sometimes a canvas is owned by a surface. If it is, getSurface() will return a bare
+     *  pointer to that surface, else this will return nullptr.
+     */
+    SkSurface* getSurface() const;
+
     /** Returns the pixel base address, SkImageInfo, rowBytes, and origin if the pixels
         can be read directly. The returned address is only valid
         while SkCanvas is in scope and unchanged. Any SkCanvas call or SkSurface call
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 7f1e979..e539714 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -20,7 +20,12 @@
 class SkColorMatrix;
 class SkColorSpace;
 struct SkStageRec;
-class SkString;
+
+namespace skvm {
+    class Builder;
+    struct F32;
+    struct Uniforms;
+}
 
 /**
  *  ColorFilters are optional objects in the drawing pipeline. When present in
@@ -56,6 +61,11 @@
 
     bool appendStages(const SkStageRec& rec, bool shaderIsOpaque) const;
 
+    virtual bool program(skvm::Builder*,
+                         SkColorSpace* dstCS,
+                         skvm::Uniforms* uniforms,
+                         skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const;
+
     enum Flags {
         /** If set the filter methods will not change the alpha channel of the colors.
         */
diff --git a/include/core/SkColorSpace.h b/include/core/SkColorSpace.h
index 9e52e85..66e99f6 100644
--- a/include/core/SkColorSpace.h
+++ b/include/core/SkColorSpace.h
@@ -141,10 +141,9 @@
     bool gammaIsLinear() const;
 
     /**
-     *  If the transfer function can be represented as coefficients to the standard
-     *  equation, returns true and sets |fn| to the proper values.
-     *
-     *  If not, returns false.
+     *  Sets |fn| to the transfer function from this color space. Returns true if the transfer
+     *  function can be represented as coefficients to the standard ICC 7-parameter equation.
+     *  Returns false otherwise (eg, PQ, HLG).
      */
     bool isNumericalTransferFn(skcms_TransferFunction* fn) const;
 
@@ -152,8 +151,6 @@
      *  Returns true and sets |toXYZD50| if the color gamut can be described as a matrix.
      *  Returns false otherwise.
      */
-    bool toXYZD50(SkMatrix44* toXYZD50) const;
-
     bool toXYZD50(skcms_Matrix3x3* toXYZD50) const;
 
     /**
@@ -220,9 +217,10 @@
      */
     static bool Equals(const SkColorSpace*, const SkColorSpace*);
 
-    void       transferFn(float gabcdef[7]) const;
-    void    invTransferFn(float gabcdef[7]) const;
-    void gamutTransformTo(const SkColorSpace* dst, float src_to_dst_row_major[9]) const;
+    void       transferFn(float gabcdef[7]) const;  // DEPRECATED: Remove when webview usage is gone
+    void       transferFn(skcms_TransferFunction* fn) const;
+    void    invTransferFn(skcms_TransferFunction* fn) const;
+    void gamutTransformTo(const SkColorSpace* dst, skcms_Matrix3x3* src_to_dst) const;
 
     uint32_t transferFnHash() const { return fTransferFnHash; }
     uint64_t           hash() const { return (uint64_t)fTransferFnHash << 32 | fToXYZD50Hash; }
@@ -230,19 +228,18 @@
 private:
     friend class SkColorSpaceSingletonFactory;
 
-    SkColorSpace(const float transferFn[7],
-                 const skcms_Matrix3x3& toXYZ);
+    SkColorSpace(const skcms_TransferFunction& transferFn, const skcms_Matrix3x3& toXYZ);
 
     void computeLazyDstFields() const;
 
     uint32_t                            fTransferFnHash;
     uint32_t                            fToXYZD50Hash;
 
-    float                               fTransferFn[7];
-    float                               fToXYZD50_3x3[9];    // row-major
+    skcms_TransferFunction              fTransferFn;
+    skcms_Matrix3x3                     fToXYZD50;
 
-    mutable float                       fInvTransferFn[7];
-    mutable float                       fFromXYZD50_3x3[9];  // row-major
+    mutable skcms_TransferFunction      fInvTransferFn;
+    mutable skcms_Matrix3x3             fFromXYZD50;
     mutable SkOnce                      fLazyDstFieldsOnce;
 };
 
diff --git a/include/core/SkDataTable.h b/include/core/SkDataTable.h
index 74e7d0e..8a4834f 100644
--- a/include/core/SkDataTable.h
+++ b/include/core/SkDataTable.h
@@ -9,7 +9,6 @@
 #define SkDataTable_DEFINED
 
 #include "include/core/SkData.h"
-#include "include/core/SkString.h"
 #include "include/private/SkTDArray.h"
 
 /**
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
index ef06c8f..6542e67 100644
--- a/include/core/SkDrawLooper.h
+++ b/include/core/SkDrawLooper.h
@@ -20,7 +20,6 @@
 class  SkCanvas;
 class  SkPaint;
 struct SkRect;
-class  SkString;
 
 /** \class SkDrawLooper
     Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are,
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index f26ace7..108a5d0 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -28,7 +28,6 @@
 class SkImageGenerator;
 class SkPaint;
 class SkPicture;
-class SkString;
 class SkSurface;
 class GrBackendTexture;
 class GrContext;
@@ -384,9 +383,7 @@
         image.
 
         @param context         GPU context
-        @param yuvColorSpace   How the YUV values are converted to RGB. One of:
-                                           kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                           kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace   How the YUV values are converted to RGB
         @param yuvaTextures    array of (up to four) YUVA textures on GPU which contain the,
                                possibly interleaved, YUVA planes
         @param yuvaIndices     array indicating which texture in yuvaTextures, and channel
@@ -409,9 +406,7 @@
         image. 'backendTexture' is used to store the result of the flattening.
 
         @param context            GPU context
-        @param yuvColorSpace      How the YUV values are converted to RGB. One of:
-                                           kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                           kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace      How the YUV values are converted to RGB
         @param yuvaTextures       array of (up to four) YUVA textures on GPU which contain the,
                                   possibly interleaved, YUVA planes
         @param yuvaIndices        array indicating which texture in yuvaTextures, and channel
@@ -442,9 +437,7 @@
         via multitexturing.
 
         @param context         GPU context
-        @param yuvColorSpace   How the YUV values are converted to RGB. One of:
-                                           kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                           kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace   How the YUV values are converted to RGB
         @param yuvaTextures    array of (up to four) YUVA textures on GPU which contain the,
                                possibly interleaved, YUVA planes
         @param yuvaIndices     array indicating which texture in yuvaTextures, and channel
@@ -474,9 +467,7 @@
         Recognized GPU formats vary by platform and GPU back-end.
 
         @param context                GPU context
-        @param yuvColorSpace          How the YUV values are converted to RGB. One of:
-                                            kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                            kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace          How the YUV values are converted to RGB
         @param yuvaPixmaps            array of (up to four) SkPixmap which contain the,
                                       possibly interleaved, YUVA planes
         @param yuvaIndices            array indicating which pixmap in yuvaPixmaps, and channel
@@ -520,8 +511,7 @@
         yuvColorSpace describes how YUV colors convert to RGB colors.
 
         @param context         GPU context
-        @param yuvColorSpace   one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                               kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace   How the YUV values are converted to RGB
         @param nv12Textures    array of YUV textures on GPU
         @param imageOrigin     one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
         @param imageColorSpace range of colors; may be nullptr
@@ -541,8 +531,7 @@
         yuvColorSpace describes how YUV colors convert to RGB colors.
 
         @param context            GPU context
-        @param yuvColorSpace      one of: kJPEG_SkYUVColorSpace, kRec601_SkYUVColorSpace,
-                                  kRec709_SkYUVColorSpace, kIdentity_SkYUVColorSpace
+        @param yuvColorSpace   How the YUV values are converted to RGB
         @param nv12Textures       array of YUV textures on GPU
         @param imageOrigin        one of: kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin
         @param backendTexture     the resource that stores the final pixels
@@ -739,6 +728,9 @@
     */
     sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy,
                                const SkMatrix* localMatrix = nullptr) const;
+    sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix& localMatrix) const {
+        return this->makeShader(tmx, tmy, &localMatrix);
+    }
 
     /** Creates SkShader from SkImage. SkShader dimensions are taken from SkImage. SkShader uses
         SkShader::kClamp_TileMode to fill drawn area outside SkImage. localMatrix permits
@@ -750,6 +742,10 @@
     sk_sp<SkShader> makeShader(const SkMatrix* localMatrix = nullptr) const {
         return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, localMatrix);
     }
+    sk_sp<SkShader> makeShader(const SkMatrix& localMatrix) const {
+        return this->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, &localMatrix);
+    }
+
 
     /** Copies SkImage pixel address, row bytes, and SkImageInfo to pixmap, if address
         is available, and returns true. If pixel address is not available, return
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index 0aba603..61f3733 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -178,10 +178,11 @@
     It can be used to visualize the YUV planes or to explicitly post process the YUV channels.
 */
 enum SkYUVColorSpace {
-    kJPEG_SkYUVColorSpace,                               //!< describes full range
-    kRec601_SkYUVColorSpace,                             //!< describes SDTV range
-    kRec709_SkYUVColorSpace,                             //!< describes HDTV range
-    kIdentity_SkYUVColorSpace,                           //!< maps Y->R, U->G, V->B
+    kJPEG_SkYUVColorSpace,                      //!< describes full range
+    kRec601_SkYUVColorSpace,                    //!< describes SDTV range
+    kRec709_SkYUVColorSpace,                    //!< describes HDTV range
+    kBT2020_SkYUVColorSpace,                    //!< describes UHDTV range, non-constant-luminance
+    kIdentity_SkYUVColorSpace,                  //!< maps Y->R, U->G, V->B
 
     kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value
 };
@@ -421,11 +422,12 @@
         Parameters are not validated to see if their values are legal, or that the
         combination is supported.
 
-        @param size  width and height, each must be zero or greater
-        @return      created SkImageInfo
+        @param dimensions  width and height, each must be zero or greater
+        @param cs          range of colors; may be nullptr
+        @return            created SkImageInfo
     */
-    static SkImageInfo MakeN32Premul(const SkISize& size) {
-        return MakeN32Premul(size.width(), size.height());
+    static SkImageInfo MakeN32Premul(SkISize dimensions, sk_sp<SkColorSpace> cs = nullptr) {
+        return Make(dimensions, kN32_SkColorType, kPremul_SkAlphaType, std::move(cs));
     }
 
     /** Creates SkImageInfo from integral dimensions width and height, kAlpha_8_SkColorType,
@@ -438,6 +440,15 @@
     static SkImageInfo MakeA8(int width, int height) {
         return Make({width, height}, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr);
     }
+    /** Creates SkImageInfo from integral dimensions, kAlpha_8_SkColorType,
+        kPremul_SkAlphaType, with SkColorSpace set to nullptr.
+
+        @param dimensions   pixel row and column count; must be zero or greater
+        @return             created SkImageInfo
+    */
+    static SkImageInfo MakeA8(SkISize dimensions) {
+        return Make(dimensions, kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr);
+    }
 
     /** Creates SkImageInfo from integral dimensions width and height, kUnknown_SkColorType,
         kUnknown_SkAlphaType, with SkColorSpace set to nullptr.
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index ffb30e4..ea7cf7c 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -15,7 +15,6 @@
 
 class SkMatrix;
 struct SkRect;
-class SkString;
 
 /** \class SkMaskFilter
 
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index a8809ec..10e0237 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -14,7 +14,6 @@
 
 struct SkRSXform;
 struct SkPoint3;
-class SkString;
 
 /** \class SkMatrix
     SkMatrix holds a 3x3 matrix for transforming coordinates. This allows mapping
@@ -90,6 +89,17 @@
         return m;
     }
 
+    /** Sets SkMatrix to translate by (t.x(), t.y()). Returned matrix is:
+
+            | 1 0 t.x() |
+            | 0 1 t.y() |
+            | 0 0 1     |
+
+        @param t  translation vector
+        @return   SkMatrix with translation
+    */
+    static SkMatrix SK_WARN_UNUSED_RESULT MakeTrans(SkVector t) { return MakeTrans(t.x(), t.y()); }
+
     /** Sets SkMatrix to:
 
             | scaleX  skewX transX |
diff --git a/include/core/SkMatrix44.h b/include/core/SkMatrix44.h
index 4e17b5f..0d595d2 100644
--- a/include/core/SkMatrix44.h
+++ b/include/core/SkMatrix44.h
@@ -336,22 +336,22 @@
                 SkMScalar m_02, SkMScalar m_12, SkMScalar m_22, SkMScalar m_32,
                 SkMScalar m_03, SkMScalar m_13, SkMScalar m_23, SkMScalar m_33);
 
-    void setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
-    void preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
-    void postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+    SkMatrix44& setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+    SkMatrix44& preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
+    SkMatrix44& postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz);
 
-    void setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
-    void preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
-    void postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+    SkMatrix44& setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+    SkMatrix44& preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
+    SkMatrix44& postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
 
-    inline void setScale(SkMScalar scale) {
-        this->setScale(scale, scale, scale);
+    inline SkMatrix44& setScale(SkMScalar scale) {
+        return this->setScale(scale, scale, scale);
     }
-    inline void preScale(SkMScalar scale) {
-        this->preScale(scale, scale, scale);
+    inline SkMatrix44& preScale(SkMScalar scale) {
+        return this->preScale(scale, scale, scale);
     }
-    inline void postScale(SkMScalar scale) {
-        this->postScale(scale, scale, scale);
+    inline SkMatrix44& postScale(SkMScalar scale) {
+        return this->postScale(scale, scale, scale);
     }
 
     void setRotateDegreesAbout(SkMScalar x, SkMScalar y, SkMScalar z,
diff --git a/include/core/SkMultiPictureDraw.h b/include/core/SkMultiPictureDraw.h
deleted file mode 100644
index 70c4e16..0000000
--- a/include/core/SkMultiPictureDraw.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkMultiPictureDraw_DEFINED
-#define SkMultiPictureDraw_DEFINED
-
-#include "include/core/SkMatrix.h"
-#include "include/private/SkTDArray.h"
-
-class SkCanvas;
-class SkPaint;
-class SkPicture;
-
-/** \class SkMultiPictureDraw
-
-    The MultiPictureDraw object accepts several picture/canvas pairs and
-    then attempts to optimally draw the pictures into the canvases, sharing
-    as many resources as possible.
-*/
-class SK_API SkMultiPictureDraw {
-public:
-    /**
-     *  Create an object to optimize the drawing of multiple pictures.
-     *  @param reserve Hint for the number of add calls expected to be issued
-     */
-    SkMultiPictureDraw(int reserve = 0);
-    ~SkMultiPictureDraw() { this->reset(); }
-
-    /**
-     *  Add a canvas/picture pair for later rendering.
-     *  @param canvas   the canvas in which to draw picture
-     *  @param picture  the picture to draw into canvas
-     *  @param matrix   if non-NULL, applied to the CTM when drawing
-     *  @param paint    if non-NULL, draw picture to a temporary buffer
-     *                  and then apply the paint when the result is drawn
-     */
-    void add(SkCanvas* canvas,
-             const SkPicture* picture,
-             const SkMatrix* matrix = nullptr,
-             const SkPaint* paint = nullptr);
-
-    /**
-     *  Perform all the previously added draws. This will reset the state
-     *  of this object. If flush is true, all canvases are flushed after
-     *  draw.
-     */
-    void draw(bool flush = false);
-
-    /**
-     *  Abandon all buffered draws and reset to the initial state.
-     */
-    void reset();
-
-private:
-    struct DrawData {
-        SkCanvas*        fCanvas;
-        const SkPicture* fPicture; // reffed
-        SkMatrix         fMatrix;
-        SkPaint*         fPaint;   // owned
-
-        void init(SkCanvas*, const SkPicture*, const SkMatrix*, const SkPaint*);
-        void draw();
-
-        static void Reset(SkTDArray<DrawData>&);
-    };
-
-    SkTDArray<DrawData> fThreadSafeDrawData;
-    SkTDArray<DrawData> fGPUDrawData;
-};
-
-#endif
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 8f69d0e..5b5a1d0 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -41,6 +41,7 @@
 class SK_API SkPath {
 public:
 
+#ifdef SK_SUPPORT_LEGACY_PATH_DIRECTION_ENUM
     /** \enum SkPath::Direction
         Direction describes whether contour is clockwise or counterclockwise.
         When SkPath contains multiple overlapping contours, Direction together with
@@ -58,6 +59,7 @@
         kCW_Direction  = static_cast<int>(SkPathDirection::kCW),
         kCCW_Direction = static_cast<int>(SkPathDirection::kCCW)
     };
+#endif
 
     /** Constructs an empty SkPath. By default, SkPath has no verbs, no SkPoint, and no weights.
         SkPath::FillType is set to kWinding_FillType.
@@ -163,6 +165,7 @@
     */
     bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const;
 
+#ifdef SK_SUPPORT_LEGACY_PATH_FILLTYPE_ENUM
     /** \enum SkPath::FillType
         FillType selects the rule used to fill SkPath. SkPath set to kWinding_FillType
         fills if the sum of contour edges is not zero, where clockwise edges add one, and
@@ -197,12 +200,62 @@
         fFillType = SkToU8(ft);
     }
 
+    /** Returns true if fill is inverted and SkPath with fill represents area outside
+        of its geometric bounds.
+
+        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
+                     kInverseWinding_FillType, kInverseEvenOdd_FillType
+        @return      true if SkPath fills outside its bounds
+    */
+    static bool IsInverseFillType(FillType fill) {
+        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
+        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
+        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
+        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
+        return (fill & 2) != 0;
+    }
+
+    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
+        .
+
+        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
+                     kInverseWinding_FillType, kInverseEvenOdd_FillType
+        @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
+    */
+    static FillType ConvertToNonInverseFillType(FillType fill) {
+        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
+        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
+        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
+        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
+        return (FillType)(fill & 1);
+    }
+#else
+    /** Returns FillType, the rule used to fill SkPath. FillType of a new SkPath is
+        kWinding_FillType.
+
+        @return  one of: kWinding_FillType, kEvenOdd_FillType,  kInverseWinding_FillType,
+                 kInverseEvenOdd_FillType
+    */
+    SkPathFillType getFillType() const { return (SkPathFillType)fFillType; }
+#endif
+    // Temporary method -- remove when we've switched to the new enum
+    SkPathFillType getNewFillType() const { return (SkPathFillType)this->getFillType(); }
+
+    /** Sets FillType, the rule used to fill SkPath. While there is no check
+        that ft is legal, values outside of FillType are not supported.
+
+        @param ft  one of: kWinding, kEvenOdd,  kInverseWinding, kInverseEvenOdd
+    */
+    void setFillType(SkPathFillType ft) {
+        fFillType = SkToU8(ft);
+    }
+
     /** Returns if FillType describes area outside SkPath geometry. The inverse fill area
         extends indefinitely.
 
         @return  true if FillType is kInverseWinding_FillType or kInverseEvenOdd_FillType
     */
-    bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
+    bool isInverseFillType() const { return SkPathFillType_IsInverse(this->getNewFillType()); }
 
     /** Replaces FillType with its inverse. The inverse of FillType describes the area
         unmodified by the original FillType.
@@ -211,6 +264,44 @@
         fFillType ^= 2;
     }
 
+    /** Returns the comvexity type, computing if needed. Never returns kUnknown.
+        @return  path's convexity type (convex or concave)
+    */
+    SkPathConvexityType getConvexityType() const {
+        SkPathConvexityType convexity = this->getConvexityTypeOrUnknown();
+        if (convexity != SkPathConvexityType::kUnknown) {
+            return convexity;
+        }
+        return this->internalGetConvexity();
+    }
+
+    /** If the path's convexity is already known, return it, else return kUnknown.
+     *  If you always want to know the convexity, even if that means having to compute it,
+     *  call getConvexitytype().
+     *
+     *  @return  known convexity, or kUnknown
+     */
+    SkPathConvexityType getConvexityTypeOrUnknown() const {
+        return (SkPathConvexityType)fConvexity.load(std::memory_order_relaxed);
+    }
+
+    /** Stores a convexity type for this path. This is what will be returned if
+     *  getConvexityTypeOrUnknown() is called. If you pass kUnknown, then if getContexityType()
+     *  is called, the real convexity will be computed.
+     *
+     *  @param convexity  one of: kUnknown, kConvex, or kConcave
+     *
+     *  example: https://fiddle.skia.org/c/@Path_setConvexity
+     */
+    void setConvexityType(SkPathConvexityType convexity);
+
+    /** Returns true if the path is convex. If necessary, it will first compute the convexity.
+     */
+    bool isConvex() const {
+        return SkPathConvexityType::kConvex == this->getConvexityType();
+    }
+
+#ifdef SK_SUPPORT_LEGACY_PATH_DIRECTION_ENUM
     /** \enum SkPath::Convexity
         SkPath is convex if it contains one contour and contour loops no more than
         360 degrees, and contour angles all have same Direction. Convex SkPath
@@ -229,53 +320,12 @@
         kConcave_Convexity = static_cast<int>(SkPathConvexityType::kConcave),
     };
 
-    /** Computes SkPath::Convexity if required, and returns stored value.
-        SkPath::Convexity is computed if stored value is kUnknown_Convexity,
-        or if SkPath has been altered since SkPath::Convexity was computed or set.
-
-        @return  computed or stored SkPath::Convexity
-    */
-    Convexity getConvexity() const {
-        Convexity convexity = this->getConvexityOrUnknown();
-        if (convexity != kUnknown_Convexity) {
-            return convexity;
-        }
-        return this->internalGetConvexity();
+    Convexity getConvexity() const { return (Convexity)this->getConvexityType(); }
+    Convexity getConvexityOrUnknown() const { return (Convexity)this->getConvexityTypeOrUnknown(); }
+    void setConvexity(Convexity convexity) {
+        this->setConvexityType((SkPathConvexityType)convexity);
     }
-
-    /** Returns last computed SkPath::Convexity, or kUnknown_Convexity if
-        SkPath has been altered since SkPath::Convexity was computed or set.
-
-        @return  stored SkPath::Convexity
-    */
-    Convexity getConvexityOrUnknown() const { return fConvexity.load(std::memory_order_relaxed); }
-
-    /** Stores convexity so that it is later returned by getConvexity() or getConvexityOrUnknown().
-        convexity may differ from getConvexity(), although setting an incorrect value may
-        cause incorrect or inefficient drawing.
-
-        If convexity is kUnknown_Convexity: getConvexity() will
-        compute SkPath::Convexity, and getConvexityOrUnknown() will return kUnknown_Convexity.
-
-        If convexity is kConvex_Convexity or kConcave_Convexity, getConvexity()
-        and getConvexityOrUnknown() will return convexity until the path is
-        altered.
-
-        @param convexity  one of: kUnknown_Convexity, kConvex_Convexity, or kConcave_Convexity
-
-        example: https://fiddle.skia.org/c/@Path_setConvexity
-    */
-    void setConvexity(Convexity convexity);
-
-    /** Computes SkPath::Convexity if required, and returns true if value is kConvex_Convexity.
-        If setConvexity() was called with kConvex_Convexity or kConcave_Convexity, and
-        the path has not been altered, SkPath::Convexity is not recomputed.
-
-        @return  true if SkPath::Convexity stored or computed is kConvex_Convexity
-    */
-    bool isConvex() const {
-        return kConvex_Convexity == this->getConvexity();
-    }
+#endif
 
     /** Returns true if this path is recognized as an oval or circle.
 
@@ -305,7 +355,7 @@
     bool isRRect(SkRRect* rrect) const;
 
     /** Sets SkPath to its initial state.
-        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding_FillType.
+        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding.
         Internal storage associated with SkPath is released.
 
         @return  reference to SkPath
@@ -315,7 +365,7 @@
     SkPath& reset();
 
     /** Sets SkPath to its initial state, preserving internal storage.
-        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding_FillType.
+        Removes verb array, SkPoint array, and weights, and sets FillType to kWinding.
         Internal storage associated with SkPath is retained.
 
         Use rewind() instead of reset() if SkPath storage will be reused and performance
@@ -968,7 +1018,7 @@
         @return             reference to SkPath
     */
     SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
-                  Direction sweep, SkScalar x, SkScalar y);
+                  SkPathDirection sweep, SkScalar x, SkScalar y);
 
     /** Appends arc to SkPath. Arc is implemented by one or more conic weighted to describe
         part of oval with radii (r.fX, r.fY) rotated by xAxisRotate degrees. Arc curves
@@ -993,7 +1043,7 @@
         @param xy           end of arc
         @return             reference to SkPath
     */
-    SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
+    SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, SkPathDirection sweep,
                const SkPoint xy) {
         return this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY);
     }
@@ -1025,7 +1075,7 @@
         @return             reference to SkPath
     */
     SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
-                   Direction sweep, SkScalar dx, SkScalar dy);
+                   SkPathDirection sweep, SkScalar dx, SkScalar dy);
 
     /** Appends kClose_Verb to SkPath. A closed contour connects the first and last SkPoint
         with line, forming a continuous loop. Open and closed contour draw the same
@@ -1041,36 +1091,6 @@
     */
     SkPath& close();
 
-    /** Returns true if fill is inverted and SkPath with fill represents area outside
-        of its geometric bounds.
-
-        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
-                     kInverseWinding_FillType, kInverseEvenOdd_FillType
-        @return      true if SkPath fills outside its bounds
-    */
-    static bool IsInverseFillType(FillType fill) {
-        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
-        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
-        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
-        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
-        return (fill & 2) != 0;
-    }
-
-    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
-        .
-
-        @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
-                     kInverseWinding_FillType, kInverseEvenOdd_FillType
-        @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
-    */
-    static FillType ConvertToNonInverseFillType(FillType fill) {
-        static_assert(0 == kWinding_FillType, "fill_type_mismatch");
-        static_assert(1 == kEvenOdd_FillType, "fill_type_mismatch");
-        static_assert(2 == kInverseWinding_FillType, "fill_type_mismatch");
-        static_assert(3 == kInverseEvenOdd_FillType, "fill_type_mismatch");
-        return (FillType)(fill & 1);
-    }
-
     /** Approximates conic with quad array. Conic is constructed from start SkPoint p0,
         control SkPoint p1, end SkPoint p2, and weight w.
         Quad array is stored in pts; this storage is supplied by caller.
@@ -1114,7 +1134,7 @@
 
         example: https://fiddle.skia.org/c/@Path_isRect
     */
-    bool isRect(SkRect* rect, bool* isClosed = nullptr, Direction* direction = nullptr) const;
+    bool isRect(SkRect* rect, bool* isClosed = nullptr, SkPathDirection* direction = nullptr) const;
 
     /** Adds SkRect to SkPath, appending kMove_Verb, three kLine_Verb, and kClose_Verb,
         starting with top-left corner of SkRect; followed by top-right, bottom-right,
@@ -1127,7 +1147,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addRect
     */
-    SkPath& addRect(const SkRect& rect, Direction dir = kCW_Direction);
+    SkPath& addRect(const SkRect& rect, SkPathDirection dir = SkPathDirection::kCW);
 
     /** Adds SkRect to SkPath, appending kMove_Verb, three kLine_Verb, and kClose_Verb.
         If dir is kCW_Direction, SkRect corners are added clockwise; if dir is
@@ -1141,7 +1161,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addRect_2
     */
-    SkPath& addRect(const SkRect& rect, Direction dir, unsigned start);
+    SkPath& addRect(const SkRect& rect, SkPathDirection dir, unsigned start);
 
     /** Adds SkRect (left, top, right, bottom) to SkPath,
         appending kMove_Verb, three kLine_Verb, and kClose_Verb,
@@ -1157,7 +1177,7 @@
         @return        reference to SkPath
     */
     SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
-                    Direction dir = kCW_Direction);
+                    SkPathDirection dir = SkPathDirection::kCW);
 
     /** Adds oval to path, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
         Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
@@ -1170,7 +1190,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addOval
     */
-    SkPath& addOval(const SkRect& oval, Direction dir = kCW_Direction);
+    SkPath& addOval(const SkRect& oval, SkPathDirection dir = SkPathDirection::kCW);
 
     /** Adds oval to SkPath, appending kMove_Verb, four kConic_Verb, and kClose_Verb.
         Oval is upright ellipse bounded by SkRect oval with radii equal to half oval width
@@ -1184,7 +1204,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addOval_2
     */
-    SkPath& addOval(const SkRect& oval, Direction dir, unsigned start);
+    SkPath& addOval(const SkRect& oval, SkPathDirection dir, unsigned start);
 
     /** Adds circle centered at (x, y) of size radius to SkPath, appending kMove_Verb,
         four kConic_Verb, and kClose_Verb. Circle begins at: (x + radius, y), continuing
@@ -1199,7 +1219,7 @@
         @return        reference to SkPath
     */
     SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius,
-                      Direction dir = kCW_Direction);
+                      SkPathDirection dir = SkPathDirection::kCW);
 
     /** Appends arc to SkPath, as the start of new contour. Arc added is part of ellipse
         bounded by oval, from startAngle through sweepAngle. Both startAngle and
@@ -1238,7 +1258,7 @@
         @return      reference to SkPath
     */
     SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
-                         Direction dir = kCW_Direction);
+                         SkPathDirection dir = SkPathDirection::kCW);
 
     /** Appends SkRRect to SkPath, creating a new closed contour. SkRRect has bounds
         equal to rect; each corner is 90 degrees of an ellipse with radii from the
@@ -1250,7 +1270,7 @@
         @return       reference to SkPath
     */
     SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[],
-                         Direction dir = kCW_Direction);
+                         SkPathDirection dir = SkPathDirection::kCW);
 
     /** Adds rrect to SkPath, creating a new closed contour. If
         dir is kCW_Direction, rrect starts at top-left of the lower-left corner and
@@ -1265,7 +1285,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addRRect
     */
-    SkPath& addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
+    SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir = SkPathDirection::kCW);
 
     /** Adds rrect to SkPath, creating a new closed contour. If dir is kCW_Direction, rrect
         winds clockwise; if dir is kCCW_Direction, rrect winds counterclockwise.
@@ -1278,7 +1298,7 @@
 
         example: https://fiddle.skia.org/c/@Path_addRRect_2
     */
-    SkPath& addRRect(const SkRRect& rrect, Direction dir, unsigned start);
+    SkPath& addRRect(const SkRRect& rrect, SkPathDirection dir, unsigned start);
 
     /** Adds contour created from line array, adding (count - 1) line segments.
         Contour added starts at pts[0], then adds a line for every additional SkPoint
@@ -1535,10 +1555,12 @@
         */
         Verb next(SkPoint pts[4]);
 
+#ifdef SK_SUPPORT_LEGACY_PATHITER_NEXT
         // DEPRECATED
         Verb next(SkPoint pts[4], bool /*doConsumeDegenerates*/, bool /*exact*/ = false) {
             return this->next(pts);
         }
+#endif
 
         /** Returns conic weight if next() returned kConic_Verb.
 
@@ -1776,10 +1798,62 @@
     */
     bool isValid() const { return this->isValidImpl() && fPathRef->isValid(); }
 
+#ifdef SK_SUPPORT_LEGACY_PATH_DIRECTION_ENUM
+    SkPath& arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
+                  Direction sweep, SkScalar x, SkScalar y) {
+        return this->arcTo(rx, ry, xAxisRotate, largeArc, (SkPathDirection)sweep, x, y);
+    }
+    SkPath& arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep,
+                  const SkPoint xy) {
+        return this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, (SkPathDirection)sweep, xy.fX, xy.fY);
+    }
+    SkPath& rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc,
+                   Direction sweep, SkScalar dx, SkScalar dy) {
+        return this->rArcTo(rx, ry, xAxisRotate, largeArc, (SkPathDirection)sweep, dx, dy);
+    }
+    bool isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
+        return this->isRect(rect, isClosed, (SkPathDirection*)direction);
+    }
+    bool isRect(SkRect* rect, bool* isClosed, nullptr_t) const {
+        return this->isRect(rect, isClosed);
+    }
+    SkPath& addRect(const SkRect& rect, Direction dir) {
+        return this->addRect(rect, (SkPathDirection)dir);
+    }
+    SkPath& addRect(const SkRect& rect, Direction dir, unsigned start) {
+        return this->addRect(rect, (SkPathDirection)dir, start);
+    }
+    SkPath& addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
+                    Direction dir) {
+        return this->addRect(left, top, right, bottom, (SkPathDirection)dir);
+    }
+    SkPath& addOval(const SkRect& oval, Direction dir) {
+        return addOval(oval, (SkPathDirection)dir);
+    }
+    SkPath& addOval(const SkRect& oval, Direction dir, unsigned start) {
+        return this->addOval(oval, (SkPathDirection)dir, start);
+    }
+    SkPath& addCircle(SkScalar x, SkScalar y, SkScalar radius, Direction dir) {
+        return this->addCircle(x, y, radius, (SkPathDirection)dir);
+    }
+    SkPath& addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) {
+        return this->addRoundRect(rect, rx, ry, (SkPathDirection)dir);
+    }
+    SkPath& addRoundRect(const SkRect& rect, const SkScalar radii[], Direction dir) {
+        return this->addRoundRect(rect, radii, (SkPathDirection)dir);
+    }
+    SkPath& addRRect(const SkRRect& rrect, Direction dir) {
+        return this->addRRect(rrect, (SkPathDirection)dir);
+    }
+    SkPath& addRRect(const SkRRect& rrect, Direction dir, unsigned start) {
+        return this->addRRect(rrect, (SkPathDirection)dir, start);
+    }
+#endif
+
 private:
     sk_sp<SkPathRef>               fPathRef;
     int                            fLastMoveToIndex;
-    mutable std::atomic<Convexity> fConvexity;
+    mutable std::atomic<uint8_t>   fConvexity;      // SkPathConvexityType
     mutable std::atomic<uint8_t>   fFirstDirection; // really an SkPathPriv::FirstDirection
     uint8_t                        fFillType    : 2;
     uint8_t                        fIsVolatile  : 1;
@@ -1820,7 +1894,7 @@
 
     inline bool hasOnlyMoveTos() const;
 
-    Convexity internalGetConvexity() const;
+    SkPathConvexityType internalGetConvexity() const;
 
     /** Asserts if SkPath data is inconsistent.
         Debugging check intended for internal use only.
@@ -1852,7 +1926,7 @@
 
     // Bottlenecks for working with fConvexity and fFirstDirection.
     // Notice the setters are const... these are mutable atomic fields.
-    void    setConvexity(Convexity) const;
+    void    setConvexityType(SkPathConvexityType) const;
     void    setFirstDirection(uint8_t) const;
     uint8_t getFirstDirection() const;
 
diff --git a/include/core/SkPathTypes.h b/include/core/SkPathTypes.h
index 4f5adf4..305c119 100644
--- a/include/core/SkPathTypes.h
+++ b/include/core/SkPathTypes.h
@@ -21,6 +21,18 @@
     kInverseEvenOdd
 };
 
+static inline bool SkPathFillType_IsEvenOdd(SkPathFillType ft) {
+    return (static_cast<int>(ft) & 1) != 0;
+}
+
+static inline bool SkPathFillType_IsInverse(SkPathFillType ft) {
+    return (static_cast<int>(ft) & 2) != 0;
+}
+
+static inline SkPathFillType SkPathFillType_ConvertToNonInverse(SkPathFillType ft) {
+    return static_cast<SkPathFillType>(static_cast<int>(ft) & 1);
+}
+
 enum class SkPathConvexityType {
     kUnknown,
     kConvex,
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index 48ad377..c64f19b 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -14,7 +14,6 @@
 #include "include/core/SkPixmap.h"
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkSize.h"
-#include "include/core/SkString.h"
 #include "include/private/SkMutex.h"
 #include "include/private/SkTDArray.h"
 
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
index f45cac3..92cb0b7 100644
--- a/include/core/SkPoint.h
+++ b/include/core/SkPoint.h
@@ -175,13 +175,13 @@
 
         @return  fX
     */
-    SkScalar x() const { return fX; }
+    constexpr SkScalar x() const { return fX; }
 
     /** Returns y-axis value of SkPoint or vector.
 
         @return  fY
     */
-    SkScalar y() const { return fY; }
+    constexpr SkScalar y() const { return fY; }
 
     /** Returns true if fX and fY are both zero.
 
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index 923d965..d9c96ac 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -197,4 +197,9 @@
     #endif
 #endif
 
+// SK_SPI is functionally identical to SK_API, but used within src to clarify that it's less stable
+#if !defined(SK_SPI)
+    #define SK_SPI SK_API
+#endif
+
 #endif
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 998a442..894cfae 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -956,10 +956,17 @@
         @param dy  added to fTop and fBottom
         @return    SkRect offset on axes, with original width and height
     */
-    SkRect makeOffset(SkScalar dx, SkScalar dy) const {
+    constexpr SkRect makeOffset(SkScalar dx, SkScalar dy) const {
         return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
     }
 
+    /** Returns SkRect offset by v.
+
+        @param v  added to rect
+        @return    SkRect offset on axes, with original width and height
+    */
+    constexpr SkRect makeOffset(SkVector v) const { return this->makeOffset(v.x(), v.y()); }
+
     /** Returns SkRect, inset by (dx, dy).
 
         If dx is negative, SkRect returned is wider.
diff --git a/include/core/SkSize.h b/include/core/SkSize.h
index 87be93d..1cfc925 100644
--- a/include/core/SkSize.h
+++ b/include/core/SkSize.h
@@ -33,6 +33,8 @@
     int32_t width() const { return fWidth; }
     int32_t height() const { return fHeight; }
 
+    int64_t area() const { return fWidth * fHeight; }
+
     bool equals(int32_t w, int32_t h) const { return fWidth == w && fHeight == h; }
 };
 
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 92703f1..bbc5597 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -849,17 +849,6 @@
                                    RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
                                    ReadPixelsCallback callback, ReadPixelsContext context);
 
-    /** Legacy version of asyncRescaleAndReadPixels() that passes data directly to the callback
-        rather than using AsyncReadResult. The data is only valid during the lifetime of the
-        callback.
-
-        Deprecated.
-     */
-    using LegacyReadPixelsCallback = void(ReadPixelsContext, const void* data, size_t rowBytes);
-    void asyncRescaleAndReadPixels(const SkImageInfo& info, const SkIRect& srcRect,
-                                   RescaleGamma rescaleGamma, SkFilterQuality rescaleQuality,
-                                   LegacyReadPixelsCallback callback, ReadPixelsContext context);
-
     /**
         Similar to asyncRescaleAndReadPixels but performs an additional conversion to YUV. The
         RGB->YUV conversion is controlled by 'yuvColorSpace'. The YUV data is returned as three
@@ -896,23 +885,6 @@
                                          ReadPixelsCallback callback,
                                          ReadPixelsContext);
 
-    /** Legacy version of asyncRescaleAndReadPixelsYUV420() that passes data directly to the
-        callback rather than using AsyncReadResult. The data is only valid during the lifetime of
-        the callback.
-
-        Deprecated.
-     */
-    using LegacyReadPixelsCallbackYUV420 = void(ReadPixelsContext, const void* data[3],
-                                                size_t rowBytes[3]);
-    void asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
-                                         sk_sp<SkColorSpace> dstColorSpace,
-                                         const SkIRect& srcRect,
-                                         int dstW, int dstH,
-                                         RescaleGamma rescaleGamma,
-                                         SkFilterQuality rescaleQuality,
-                                         LegacyReadPixelsCallbackYUV420 callback,
-                                         ReadPixelsContext);
-
     /** Copies SkRect of pixels from the src SkPixmap to the SkSurface.
 
         Source SkRect corners are (0, 0) and (src.width(), src.height()).
diff --git a/include/docs/SkPDFDocument.h b/include/docs/SkPDFDocument.h
index c25c2b9..892489e 100644
--- a/include/docs/SkPDFDocument.h
+++ b/include/docs/SkPDFDocument.h
@@ -164,6 +164,9 @@
     SkExecutor* fExecutor = nullptr;
 
     /** Preferred Subsetter. Only respected if both are compiled in.
+
+        The Sfntly subsetter is deprecated.
+
         Experimental.
     */
     enum Subsetter {
diff --git a/include/effects/SkColorMatrix.h b/include/effects/SkColorMatrix.h
index f2b7964..334a86d 100644
--- a/include/effects/SkColorMatrix.h
+++ b/include/effects/SkColorMatrix.h
@@ -9,30 +9,29 @@
 #define SkColorMatrix_DEFINED
 
 #include "include/core/SkTypes.h"
-#include <memory.h>
+
+#include <algorithm>
+#include <array>
 
 class SK_API SkColorMatrix {
 public:
+    constexpr SkColorMatrix() : SkColorMatrix(1, 0, 0, 0, 0,
+                                              0, 1, 0, 0, 0,
+                                              0, 0, 1, 0, 0,
+                                              0, 0, 0, 1, 0) {}
+
+    constexpr SkColorMatrix(float m00, float m01, float m02, float m03, float m04,
+                            float m10, float m11, float m12, float m13, float m14,
+                            float m20, float m21, float m22, float m23, float m24,
+                            float m30, float m31, float m32, float m33, float m34)
+        : fMat { m00, m01, m02, m03, m04,
+                 m10, m11, m12, m13, m14,
+                 m20, m21, m22, m23, m24,
+                 m30, m31, m32, m33, m34 } {}
+
     void setIdentity();
     void setScale(float rScale, float gScale, float bScale, float aScale = 1.0f);
 
-    void setRowMajor(const float src[20]) {
-        memcpy(fMat, src, sizeof(fMat));
-    }
-
-    void getRowMajor(float dst[20]) const {
-        memcpy(dst, fMat, sizeof(fMat));
-    }
-
-    enum Axis {
-        kR_Axis = 0,
-        kG_Axis = 1,
-        kB_Axis = 2
-    };
-    void setRotate(Axis, float degrees);
-    void setSinCos(Axis, float sine, float cosine);
-    void preRotate(Axis, float degrees);
-    void postRotate(Axis, float degrees);
     void postTranslate(float dr, float dg, float db, float da);
 
     void setConcat(const SkColorMatrix& a, const SkColorMatrix& b);
@@ -40,25 +39,12 @@
     void postConcat(const SkColorMatrix& mat) { this->setConcat(mat, *this); }
 
     void setSaturation(float sat);
-    void setRGB2YUV();
-    void setYUV2RGB();
 
-    bool operator==(const SkColorMatrix& other) const {
-        return 0 == memcmp(fMat, other.fMat, sizeof(fMat));
-    }
-
-    bool operator!=(const SkColorMatrix& other) const { return !((*this) == other); }
-
-    float* get20(float m[20]) const {
-        memcpy(m, fMat, sizeof(fMat));
-        return m;
-    }
-    void set20(const float m[20]) {
-        memcpy(fMat, m, sizeof(fMat));
-    }
+    void setRowMajor(const float src[20]) { std::copy_n(src, 20, fMat.begin()); }
+    void getRowMajor(float dst[20]) const { std::copy_n(fMat.begin(), 20, dst); }
 
 private:
-    float fMat[20];
+    std::array<float, 20> fMat;
 
     friend class SkColorFilters;
 };
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
index e98f6af..4983eb8 100644
--- a/include/gpu/GrBackendSurface.h
+++ b/include/gpu/GrBackendSurface.h
@@ -72,7 +72,7 @@
     static GrBackendFormat MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo);
 
 #ifdef SK_DAWN
-    static GrBackendFormat MakeDawn(dawn::TextureFormat format) {
+    static GrBackendFormat MakeDawn(wgpu::TextureFormat format) {
         return GrBackendFormat(format);
     }
 #endif
@@ -109,10 +109,10 @@
 
 #ifdef SK_DAWN
     /**
-     * If the backend API is Dawn this gets the format as a dawn::TextureFormat and returns true.
+     * If the backend API is Dawn this gets the format as a wgpu::TextureFormat and returns true.
      * Otherwise, returns false.
      */
-    bool asDawnFormat(dawn::TextureFormat*) const;
+    bool asDawnFormat(wgpu::TextureFormat*) const;
 #endif
 
 #ifdef SK_METAL
@@ -147,7 +147,7 @@
     GrBackendFormat(const VkFormat vkFormat, const GrVkYcbcrConversionInfo&);
 
 #ifdef SK_DAWN
-    GrBackendFormat(dawn::TextureFormat format);
+    GrBackendFormat(wgpu::TextureFormat format);
 #endif
 
 #ifdef SK_METAL
@@ -166,7 +166,7 @@
             GrVkYcbcrConversionInfo  fYcbcrConversionInfo;
         }                fVk;
 #ifdef SK_DAWN
-        dawn::TextureFormat fDawnFormat;
+        wgpu::TextureFormat fDawnFormat;
 #endif
 
 #ifdef SK_METAL
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 4051954..d89b2db 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -72,8 +72,8 @@
 #endif
 
 #ifdef SK_DAWN
-    static sk_sp<GrContext> MakeDawn(const dawn::Device& device, const GrContextOptions& options);
-    static sk_sp<GrContext> MakeDawn(const dawn::Device& device);
+    static sk_sp<GrContext> MakeDawn(const wgpu::Device& device, const GrContextOptions& options);
+    static sk_sp<GrContext> MakeDawn(const wgpu::Device& device);
 #endif
 
     static sk_sp<GrContext> MakeMock(const GrMockOptions*, const GrContextOptions&);
@@ -365,7 +365,8 @@
     void storeVkPipelineCacheData();
 
     // Returns the gpu memory size of the the texture that backs the passed in SkImage. Returns 0 if
-    // the SkImage is not texture backed.
+    // the SkImage is not texture backed. For external format textures this will also return 0 as we
+    // cannot determine the correct size.
     static size_t ComputeImageSize(sk_sp<SkImage> image, GrMipMapped, bool useNextPow2 = false);
 
     /*
diff --git a/include/gpu/dawn/GrDawnTypes.h b/include/gpu/dawn/GrDawnTypes.h
index f8936c2..7ec9b3d 100644
--- a/include/gpu/dawn/GrDawnTypes.h
+++ b/include/gpu/dawn/GrDawnTypes.h
@@ -10,12 +10,21 @@
 
 #ifdef Always
 #undef Always
+static constexpr int Always = 2;
+#endif
+#ifdef Success
+#undef Success
+static constexpr int Success = 0;
+#endif
+#ifdef None
+#undef None
+static constexpr int None = 0L;
 #endif
 #include "dawn/dawncpp.h"
 
 struct GrDawnImageInfo {
-    dawn::Texture       fTexture;
-    dawn::TextureFormat fFormat;
+    wgpu::Texture       fTexture;
+    wgpu::TextureFormat fFormat;
     uint32_t            fLevelCount;
     GrDawnImageInfo() : fTexture(nullptr), fFormat(), fLevelCount(0) {
     }
diff --git a/include/private/GrRecordingContext.h b/include/private/GrRecordingContext.h
index 39d851c..10d281b 100644
--- a/include/private/GrRecordingContext.h
+++ b/include/private/GrRecordingContext.h
@@ -50,10 +50,10 @@
     sk_sp<GrOpMemoryPool> refOpMemoryPool();
     GrOpMemoryPool* opMemoryPool();
 
-    SkArenaAlloc* opPODAllocator();
-    // This entry point should only be used for DDL creation where we want the ops' POD lifetime
+    SkArenaAlloc* recordTimeAllocator();
+    // This entry point should only be used for DDL creation where we want the ops' data's lifetime
     // to match that of the DDL.
-    std::unique_ptr<SkArenaAlloc> detachOpPOD();
+    std::unique_ptr<SkArenaAlloc> detachRecordTimeAllocator();
 
     GrStrikeCache* getGrStrikeCache() { return fStrikeCache.get(); }
     GrTextBlobCache* getTextBlobCache();
@@ -131,7 +131,7 @@
     std::unique_ptr<GrDrawingManager> fDrawingManager;
     // All the GrOp-derived classes use this pool.
     sk_sp<GrOpMemoryPool>             fOpMemoryPool;
-    std::unique_ptr<SkArenaAlloc>     fOpPODAllocator;
+    std::unique_ptr<SkArenaAlloc>     fRecordTimeAllocator;
 
     std::unique_ptr<GrStrikeCache>    fStrikeCache;
     std::unique_ptr<GrTextBlobCache>  fTextBlobCache;
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 5836525..255da0b 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -242,12 +242,12 @@
 };
 
 inline GrFillRule GrFillRuleForSkPath(const SkPath& path) {
-    switch (path.getFillType()) {
-        case SkPath::kWinding_FillType:
-        case SkPath::kInverseWinding_FillType:
+    switch (path.getNewFillType()) {
+        case SkPathFillType::kWinding:
+        case SkPathFillType::kInverseWinding:
             return GrFillRule::kNonzero;
-        case SkPath::kEvenOdd_FillType:
-        case SkPath::kInverseEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
+        case SkPathFillType::kInverseEvenOdd:
             return GrFillRule::kEvenOdd;
     }
     SkUNREACHABLE;
@@ -859,7 +859,7 @@
     kRG_F16,
     kRGBA_16161616,
 
-    // Unusual formats that come up after reading back in cases where we are reassigning the meaning
+    // Unusual types that come up after reading back in cases where we are reassigning the meaning
     // of a texture format's channels to use for a particular color format but have to read back the
     // data to a full RGBA quadruple. (e.g. using a R8 texture format as A8 color type but the API
     // only supports reading to RGBA8.) None of these have SkColorType equivalents.
@@ -867,7 +867,14 @@
     kAlpha_F32xxx,
     kGray_8xxx,
 
-    kLast = kGray_8xxx
+    // Types used to initialize backend textures.
+    kRGB_888,
+    kR_8,
+    kR_16,
+    kR_F16,
+    kGray_F16,
+
+    kLast = kGray_F16
 };
 
 static const int kGrColorTypeCnt = static_cast<int>(GrColorType::kLast) + 1;
@@ -897,6 +904,11 @@
         case GrColorType::kRG_1616:          return kR16G16_unorm_SkColorType;
         case GrColorType::kRGBA_16161616:    return kR16G16B16A16_unorm_SkColorType;
         case GrColorType::kRG_F16:           return kR16G16_float_SkColorType;
+        case GrColorType::kRGB_888:          return kUnknown_SkColorType;
+        case GrColorType::kR_8:              return kUnknown_SkColorType;
+        case GrColorType::kR_16:             return kUnknown_SkColorType;
+        case GrColorType::kR_F16:            return kUnknown_SkColorType;
+        case GrColorType::kGray_F16:         return kUnknown_SkColorType;
     }
     SkUNREACHABLE;
 }
@@ -959,6 +971,11 @@
         case GrColorType::kRGBA_16161616:    return kRGBA_SkColorTypeComponentFlags;
         case GrColorType::kRG_F16:           return kRed_SkColorTypeComponentFlag |
                                                     kGreen_SkColorTypeComponentFlag;
+        case GrColorType::kRGB_888:          return kRGB_SkColorTypeComponentFlags;
+        case GrColorType::kR_8:              return kRed_SkColorTypeComponentFlag;
+        case GrColorType::kR_16:             return kRed_SkColorTypeComponentFlag;
+        case GrColorType::kR_F16:            return kRed_SkColorTypeComponentFlag;
+        case GrColorType::kGray_F16:         return kGray_SkColorTypeComponentFlag;
     }
     SkUNREACHABLE;
 }
@@ -1098,6 +1115,16 @@
             return GrColorTypeDesc::MakeRGBA(16, GrColorTypeEncoding::kUnorm);
         case GrColorType::kRG_F16:
             return GrColorTypeDesc::MakeRG(16, GrColorTypeEncoding::kFloat);
+        case GrColorType::kRGB_888:
+            return GrColorTypeDesc::MakeRGB(8, GrColorTypeEncoding::kUnorm);
+        case GrColorType::kR_8:
+            return GrColorTypeDesc::MakeR(8, GrColorTypeEncoding::kUnorm);
+        case GrColorType::kR_16:
+            return GrColorTypeDesc::MakeR(16, GrColorTypeEncoding::kUnorm);
+        case GrColorType::kR_F16:
+            return GrColorTypeDesc::MakeR(16, GrColorTypeEncoding::kFloat);
+        case GrColorType::kGray_F16:
+            return GrColorTypeDesc::MakeGray(16, GrColorTypeEncoding::kFloat);
     }
     SkUNREACHABLE;
 }
@@ -1154,6 +1181,11 @@
         case GrColorType::kRG_1616:          return 4;
         case GrColorType::kRGBA_16161616:    return 8;
         case GrColorType::kRG_F16:           return 4;
+        case GrColorType::kRGB_888:          return 3;
+        case GrColorType::kR_8:              return 1;
+        case GrColorType::kR_16:             return 2;
+        case GrColorType::kR_F16:            return 2;
+        case GrColorType::kGray_F16:         return 2;
     }
     SkUNREACHABLE;
 }
@@ -1241,6 +1273,11 @@
         case GrColorType::kRG_1616:          return kRG_1616_GrPixelConfig;
         case GrColorType::kRGBA_16161616:    return kRGBA_16161616_GrPixelConfig;
         case GrColorType::kRG_F16:           return kRG_half_GrPixelConfig;
+        case GrColorType::kRGB_888:          return kUnknown_GrPixelConfig;
+        case GrColorType::kR_8:              return kUnknown_GrPixelConfig;
+        case GrColorType::kR_16:             return kUnknown_GrPixelConfig;
+        case GrColorType::kR_F16:            return kUnknown_GrPixelConfig;
+        case GrColorType::kGray_F16:         return kUnknown_GrPixelConfig;
     }
     SkUNREACHABLE;
 }
@@ -1301,6 +1338,11 @@
         case GrColorType::kRG_1616:          return "kRG_1616";
         case GrColorType::kRGBA_16161616:    return "kRGBA_16161616";
         case GrColorType::kRG_F16:           return "kRG_F16";
+        case GrColorType::kRGB_888:          return "kRGB_888";
+        case GrColorType::kR_8:              return "kR_8";
+        case GrColorType::kR_16:             return "kR_16";
+        case GrColorType::kR_F16:            return "kR_F16";
+        case GrColorType::kGray_F16:         return "kGray_F16";
     }
     SkUNREACHABLE;
 }
diff --git a/include/private/SkColorData.h b/include/private/SkColorData.h
index 00a2895..2090ab4 100644
--- a/include/private/SkColorData.h
+++ b/include/private/SkColorData.h
@@ -437,8 +437,8 @@
 constexpr SkPMColor4f SK_PMColor4fTRANSPARENT = { 0, 0, 0, 0 };
 constexpr SkPMColor4f SK_PMColor4fWHITE = { 1, 1, 1, 1 };
 constexpr SkPMColor4f SK_PMColor4fILLEGAL = { SK_FloatNegativeInfinity,
-                                                  SK_FloatNegativeInfinity,
-                                                  SK_FloatNegativeInfinity,
-                                                  SK_FloatNegativeInfinity };
+                                              SK_FloatNegativeInfinity,
+                                              SK_FloatNegativeInfinity,
+                                              SK_FloatNegativeInfinity };
 
 #endif
diff --git a/include/private/SkDeferredDisplayList.h b/include/private/SkDeferredDisplayList.h
index b90995c..ccf1d08 100644
--- a/include/private/SkDeferredDisplayList.h
+++ b/include/private/SkDeferredDisplayList.h
@@ -71,7 +71,7 @@
 
     SkTArray<sk_sp<GrRenderTask>>   fRenderTasks;
     PendingPathsMap                 fPendingPaths;  // This is the path data from CCPR.
-    std::unique_ptr<SkArenaAlloc>   fOpPOD;
+    std::unique_ptr<SkArenaAlloc>   fRecordTimeData;
 #endif
     sk_sp<LazyProxyData>            fLazyProxyData;
 };
diff --git a/include/private/SkImageInfoPriv.h b/include/private/SkImageInfoPriv.h
index d0ff6fd..15f0224 100644
--- a/include/private/SkImageInfoPriv.h
+++ b/include/private/SkImageInfoPriv.h
@@ -58,13 +58,6 @@
     return value <= kLastEnum_SkAlphaType;
 }
 
-static inline bool SkColorTypeIsGray(SkColorType ct) {
-    auto flags = SkColorTypeComponentFlags(ct);
-    // Currently assuming that a color type has only gray or does not have gray.
-    SkASSERT(!(kGray_SkColorTypeComponentFlag & flags) || kGray_SkColorTypeComponentFlag == flags);
-    return kGray_SkColorTypeComponentFlag == flags;
-}
-
 static int SkColorTypeShiftPerPixel(SkColorType ct) {
     switch (ct) {
         case kUnknown_SkColorType:            return 0;
@@ -105,6 +98,32 @@
     return y * rowBytes + (x << SkColorTypeShiftPerPixel(ct));
 }
 
+static inline bool SkColorTypeIsNormalized(SkColorType ct) {
+    switch (ct) {
+        case kUnknown_SkColorType:
+        case kAlpha_8_SkColorType:
+        case kRGB_565_SkColorType:
+        case kARGB_4444_SkColorType:
+        case kRGBA_8888_SkColorType:
+        case kRGB_888x_SkColorType:
+        case kBGRA_8888_SkColorType:
+        case kRGBA_1010102_SkColorType:
+        case kRGB_101010x_SkColorType:
+        case kGray_8_SkColorType:
+        case kRGBA_F16Norm_SkColorType:
+        case kR8G8_unorm_SkColorType:
+        case kA16_unorm_SkColorType:
+        case kA16_float_SkColorType:          /*subtle... alpha is always [0,1]*/
+        case kR16G16_unorm_SkColorType:
+        case kR16G16B16A16_unorm_SkColorType: return true;
+
+        case kRGBA_F16_SkColorType:
+        case kRGBA_F32_SkColorType:
+        case kR16G16_float_SkColorType:       return false;
+    }
+    SkUNREACHABLE;
+}
+
 /**
  *  Returns true if |info| contains a valid combination of width, height, colorType, and alphaType.
  */
diff --git a/include/private/SkOnce.h b/include/private/SkOnce.h
index 662bffb..edf3e83 100644
--- a/include/private/SkOnce.h
+++ b/include/private/SkOnce.h
@@ -8,6 +8,7 @@
 #ifndef SkOnce_DEFINED
 #define SkOnce_DEFINED
 
+#include "include/private/SkThreadAnnotations.h"
 #include <atomic>
 #include <utility>
 
@@ -39,7 +40,9 @@
 
         // Some other thread is calling fn().
         // We'll just spin here acquiring until it releases Done into fState.
+        SK_POTENTIALLY_BLOCKING_REGION_BEGIN;
         while (fState.load(std::memory_order_acquire) != Done) { /*spin*/ }
+        SK_POTENTIALLY_BLOCKING_REGION_END;
     }
 
 private:
diff --git a/include/private/SkSemaphore.h b/include/private/SkSemaphore.h
index 29bbca6f..e4ca9e8 100644
--- a/include/private/SkSemaphore.h
+++ b/include/private/SkSemaphore.h
@@ -10,6 +10,7 @@
 
 #include "include/core/SkTypes.h"
 #include "include/private/SkOnce.h"
+#include "include/private/SkThreadAnnotations.h"
 #include <atomic>
 
 class SkSemaphore {
@@ -72,7 +73,9 @@
     // Since this fetches the value before the subtract, zero and below means that there are no
     // resources left, so the thread needs to wait.
     if (fCount.fetch_sub(1, std::memory_order_acquire) <= 0) {
+        SK_POTENTIALLY_BLOCKING_REGION_BEGIN;
         this->osWait();
+        SK_POTENTIALLY_BLOCKING_REGION_END;
     }
 }
 
diff --git a/include/private/SkTArray.h b/include/private/SkTArray.h
index c70164a..3b437bb 100644
--- a/include/private/SkTArray.h
+++ b/include/private/SkTArray.h
@@ -49,7 +49,7 @@
     SkTArray(SkTArray&& that) {
         // TODO: If 'that' owns its memory why don't we just steal the pointer?
         this->init(that.fCount);
-        that.move(fMemArray);
+        that.move(fItemArray);
         that.fCount = 0;
     }
 
@@ -86,7 +86,7 @@
         fCount = 0;
         this->checkRealloc(that.count());
         fCount = that.count();
-        that.move(fMemArray);
+        that.move(fItemArray);
         that.fCount = 0;
         return *this;
     }
@@ -96,7 +96,7 @@
             fItemArray[i].~T();
         }
         if (fOwnMemory) {
-            sk_free(fMemArray);
+            sk_free(fItemArray);
         }
     }
 
@@ -428,7 +428,7 @@
     template <int N>
     SkTArray(SkTArray&& array, SkAlignedSTStorage<N,T>* storage) {
         this->initWithPreallocatedStorage(array.fCount, storage->get(), N);
-        array.move(fMemArray);
+        array.move(fItemArray);
         array.fCount = 0;
     }
 
@@ -450,12 +450,12 @@
         fCount = count;
         if (!count && !reserveCount) {
             fAllocCount = 0;
-            fMemArray = nullptr;
+            fItemArray = nullptr;
             fOwnMemory = true;
             fReserved = false;
         } else {
             fAllocCount = SkTMax(count, SkTMax(kMinHeapAllocCount, reserveCount));
-            fMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
+            fItemArray = (T*)sk_malloc_throw(fAllocCount, sizeof(T));
             fOwnMemory = true;
             fReserved = reserveCount > 0;
         }
@@ -466,15 +466,15 @@
         SkASSERT(preallocCount > 0);
         SkASSERT(preallocStorage);
         fCount = count;
-        fMemArray = nullptr;
+        fItemArray = nullptr;
         fReserved = false;
         if (count > preallocCount) {
             fAllocCount = SkTMax(count, kMinHeapAllocCount);
-            fMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
+            fItemArray = (T*)sk_malloc_throw(fAllocCount, sizeof(T));
             fOwnMemory = true;
         } else {
             fAllocCount = preallocCount;
-            fMemArray = preallocStorage;
+            fItemArray = (T*)preallocStorage;
             fOwnMemory = false;
         }
     }
@@ -496,7 +496,7 @@
         memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T));
     }
     template <bool E = MEM_MOVE> SK_WHEN(E, void) move(void* dst) {
-        sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T));
+        sk_careful_memcpy(dst, fItemArray, fCount * sizeof(T));
     }
 
     template <bool E = MEM_MOVE> SK_WHEN(!E, void) move(int dst, int src) {
@@ -551,21 +551,18 @@
 
         fAllocCount = Sk64_pin_to_s32(newAllocCount);
         SkASSERT(fAllocCount >= newCount);
-        void* newMemArray = sk_malloc_throw(fAllocCount, sizeof(T));
-        this->move(newMemArray);
+        T* newItemArray = (T*)sk_malloc_throw(fAllocCount, sizeof(T));
+        this->move(newItemArray);
         if (fOwnMemory) {
-            sk_free(fMemArray);
+            sk_free(fItemArray);
 
         }
-        fMemArray = newMemArray;
+        fItemArray = newItemArray;
         fOwnMemory = true;
         fReserved = false;
     }
 
-    union {
-        T*       fItemArray;
-        void*    fMemArray;
-    };
+    T* fItemArray;
     int fCount;
     int fAllocCount;
     bool fOwnMemory : 1;
diff --git a/include/private/SkTHash.h b/include/private/SkTHash.h
index bc563d1..d04a2cc 100644
--- a/include/private/SkTHash.h
+++ b/include/private/SkTHash.h
@@ -274,6 +274,13 @@
         return nullptr;
     }
 
+    V& operator[](const K& key) {
+        if (V* val = this->find(key)) {
+            return *val;
+        }
+        return *this->set(key, V{});
+    }
+
     // Remove the key/value entry in the table with this key.
     void remove(const K& key) {
         SkASSERT(this->find(key));
diff --git a/include/private/SkTLogic.h b/include/private/SkTLogic.h
index f128a46..8cf8c28 100644
--- a/include/private/SkTLogic.h
+++ b/include/private/SkTLogic.h
@@ -62,9 +62,9 @@
 struct monostate {};
 
 template<typename...> struct conjunction : std::true_type { };
-template<typename B0> struct conjunction<B0> : B0 { };
-template<typename B0, typename... Bs>
-struct conjunction<B0, Bs...> : std::conditional<bool(B0::value), conjunction<Bs...>, B0>::type { };
+template<typename T> struct conjunction<T> : T { };
+template<typename T, typename... Ts>
+struct conjunction<T, Ts...> : std::conditional<bool(T::value), conjunction<Ts...>, T>::type { };
 }  // namespace skstd
 
 // The sknonstd namespace contains things we would like to be proposed and feel std-ish.
diff --git a/include/private/SkThreadAnnotations.h b/include/private/SkThreadAnnotations.h
index fd312b5..29e07c4 100644
--- a/include/private/SkThreadAnnotations.h
+++ b/include/private/SkThreadAnnotations.h
@@ -76,5 +76,16 @@
 #define SK_NO_THREAD_SAFETY_ANALYSIS \
   SK_THREAD_ANNOTATION_ATTRIBUTE(no_thread_safety_analysis)
 
+#if defined(SK_BUILD_FOR_GOOGLE3)
+    extern "C" {
+        void __google_potentially_blocking_region_begin(void);
+        void __google_potentially_blocking_region_end  (void);
+    }
+    #define SK_POTENTIALLY_BLOCKING_REGION_BEGIN __google_potentially_blocking_region_begin()
+    #define SK_POTENTIALLY_BLOCKING_REGION_END   __google_potentially_blocking_region_end()
+#else
+    #define SK_POTENTIALLY_BLOCKING_REGION_BEGIN
+    #define SK_POTENTIALLY_BLOCKING_REGION_END
+#endif
 
 #endif  // SkThreadAnnotations_DEFINED
diff --git a/include/third_party/skcms/skcms.h b/include/third_party/skcms/skcms.h
index f458784..3df6b44 100644
--- a/include/third_party/skcms/skcms.h
+++ b/include/third_party/skcms/skcms.h
@@ -176,15 +176,8 @@
                                       skcms_TransferFunction* approx,
                                       float* max_error);
 
-typedef struct skcms_ICCTag {
-    uint32_t       signature;
-    uint32_t       type;
-    uint32_t       size;
-    const uint8_t* buf;
-} skcms_ICCTag;
-
-SKCMS_API void skcms_GetTagByIndex    (const skcms_ICCProfile*, uint32_t idx, skcms_ICCTag*);
-SKCMS_API bool skcms_GetTagBySignature(const skcms_ICCProfile*, uint32_t sig, skcms_ICCTag*);
+SKCMS_API bool skcms_GetCHAD(const skcms_ICCProfile*, skcms_Matrix3x3*);
+SKCMS_API bool skcms_GetWTPT(const skcms_ICCProfile*, float xyz[3]);
 
 // These are common ICC signature values
 enum {
diff --git a/include/utils/mac/SkCGUtils.h b/include/utils/mac/SkCGUtils.h
index a8bde99..43cd59f 100644
--- a/include/utils/mac/SkCGUtils.h
+++ b/include/utils/mac/SkCGUtils.h
@@ -74,13 +74,5 @@
  */
 void SkCGDrawBitmap(CGContextRef, const SkBitmap&, float x, float y);
 
-/**
- *  Return a provider that wraps the specified stream.
- *  When the provider is finally deleted, it will delete the stream.
- */
-CGDataProviderRef SkCreateDataProviderFromStream(std::unique_ptr<SkStreamRewindable>);
-
-CGDataProviderRef SkCreateDataProviderFromData(sk_sp<SkData>);
-
 #endif  // defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
 #endif  // SkCGUtils_DEFINED
diff --git a/infra/bots/android_compile/trigger_wait_ac_task.py b/infra/bots/android_compile/trigger_wait_ac_task.py
old mode 100644
new mode 100755
index 538b4f2..6a6e2b7
--- a/infra/bots/android_compile/trigger_wait_ac_task.py
+++ b/infra/bots/android_compile/trigger_wait_ac_task.py
@@ -11,7 +11,6 @@
 import math
 import optparse
 import os
-import requests
 import subprocess
 import sys
 import time
@@ -19,6 +18,7 @@
 INFRA_BOTS_DIR = os.path.abspath(os.path.realpath(os.path.join(
     os.path.dirname(os.path.abspath(__file__)), os.pardir)))
 sys.path.insert(0, INFRA_BOTS_DIR)
+import git_utils
 import utils
 
 
@@ -109,6 +109,14 @@
   return task
 
 
+def _add_cl_comment(issue, comment):
+  # Depot tools needs a checkout to use "git cl" even though we are just adding
+  # a comment to a change unrelated to the checkout.
+  with git_utils.NewGitCheckout(repository=utils.SKIA_REPO) as checkout:
+    add_comment_cmd = ['git', 'cl', 'comments', '-i', str(issue), '-a', comment]
+    subprocess.check_call(add_comment_cmd)
+
+
 def trigger_and_wait(options):
   """Triggers a task on the compile server and waits for it to complete."""
   task = _trigger_task(options)
@@ -173,11 +181,16 @@
                    ret['withpatch_log'], ret['nopatch_log'],
                    'https://skia-android-compile.corp.goog/'))
       else:
-        print ('Both with patch and no patch builds failed. This means that the'
-               ' Android tree is currently broken. Marking this bot as '
-               'successful')
-        print 'With patch logs are here: %s' % ret['withpatch_log']
-        print 'No patch logs are here: %s' % ret['nopatch_log']
+        msg = ('FYI: Both with patch and no patch builds of the %s bot '
+               'failed.\nThis could mean that the Android tree is currently '
+               'broken and infra is investigating.\nMarking this bot as '
+               'successful to not block the CQ.\n\n'
+               'With patch logs are here: %s\n'
+               'No patch logs are here: %s\n\n') % (
+                   options.builder_name, ret['withpatch_log'],
+                   ret['nopatch_log'])
+        _add_cl_comment(task['issue'], msg)
+        print msg
         return 0
 
     # Print status of the task.
@@ -213,6 +226,9 @@
   option_parser.add_option(
       '', '--hash', type=str, default='',
       help='The Skia repo hash to compile against.')
+  option_parser.add_option(
+      '', '--builder_name', type=str, default='',
+      help='The builder that triggered this run.')
   options, _ = option_parser.parse_args()
   sys.exit(trigger_and_wait(options))
 
diff --git a/infra/bots/assets/asset_utils.py b/infra/bots/assets/asset_utils.py
old mode 100644
new mode 100755
index 686720c..9dd9f45
--- a/infra/bots/assets/asset_utils.py
+++ b/infra/bots/assets/asset_utils.py
@@ -44,11 +44,16 @@
 
 class CIPDStore(object):
   """Wrapper object for CIPD."""
-  def __init__(self, cipd_url=DEFAULT_CIPD_SERVICE_URL):
+  def __init__(self, cipd_url=DEFAULT_CIPD_SERVICE_URL,
+               service_account_json=None):
     self._cipd = 'cipd'
     if sys.platform == 'win32':
       self._cipd = 'cipd.bat'
     self._cipd_url = cipd_url
+    if service_account_json:
+      self._service_account_json = os.path.abspath(service_account_json)
+    else:
+      self._service_account_json = None
     self._check_setup()
 
   def _check_setup(self):
@@ -67,7 +72,9 @@
     cipd_args = []
     if specify_service_url:
       cipd_args.extend(['--service-url', self._cipd_url])
-    if os.getenv('USE_CIPD_GCE_AUTH'):
+    if self._service_account_json:
+      cipd_args.extend(['-service-account-json', self._service_account_json])
+    elif os.getenv('USE_CIPD_GCE_AUTH'):
       # Enable automatic GCE authentication. For context see
       # https://bugs.chromium.org/p/skia/issues/detail?id=6385#c3
       cipd_args.extend(['-service-account-json', ':gce'])
@@ -153,7 +160,8 @@
 
 class GSStore(object):
   """Wrapper object for interacting with Google Storage."""
-  def __init__(self, gsutil=None, bucket=DEFAULT_GS_BUCKET):
+  def __init__(self, gsutil=None, bucket=DEFAULT_GS_BUCKET,
+               service_account_json=None):
     if gsutil:
       gsutil = os.path.abspath(gsutil)
     else:
@@ -171,6 +179,9 @@
     self._gsutil = [gsutil]
     if gsutil.endswith('.py'):
       self._gsutil = ['python', gsutil]
+    if service_account_json:
+      sa = os.path.abspath(service_account_json)
+      self._gsutil += ['-o', 'Credentials:gs_service_key_file=' + sa]
     self._gs_bucket = bucket
 
   def copy(self, src, dst):
@@ -230,9 +241,12 @@
 class MultiStore(object):
   """Wrapper object which uses CIPD as the primary store and GS for backup."""
   def __init__(self, cipd_url=DEFAULT_CIPD_SERVICE_URL,
+               service_account_json=None,
                gsutil=None, gs_bucket=DEFAULT_GS_BUCKET):
-    self._cipd = CIPDStore(cipd_url=cipd_url)
-    self._gs = GSStore(gsutil=gsutil, bucket=gs_bucket)
+    self._cipd = CIPDStore(cipd_url=cipd_url,
+                           service_account_json=service_account_json)
+    self._gs = GSStore(gsutil=gsutil, bucket=gs_bucket,
+                       service_account_json=service_account_json)
 
   def get_available_versions(self, name):
     return self._cipd.get_available_versions(name)
diff --git a/infra/bots/assets/asset_utils_test.py b/infra/bots/assets/asset_utils_test.py
old mode 100644
new mode 100755
diff --git a/infra/bots/assets/assets.py b/infra/bots/assets/assets.py
index f29ddec..a5284dd 100755
--- a/infra/bots/assets/assets.py
+++ b/infra/bots/assets/assets.py
@@ -23,31 +23,43 @@
 import utils
 
 
+def _common_args(prs):
+  """Add common args to the given argparse.ArgumentParser."""
+  prs.add_argument('asset_name', help='Name of the asset.')
+  prs.add_argument('--gsutil')
+  prs.add_argument('--service_account_json')
+
+
+def _store(args):
+  """Return asset_utils.MultiStore based on args."""
+  return asset_utils.MultiStore(gsutil=args.gsutil,
+                                service_account_json=args.service_account_json)
+
+
+def _asset(args):
+  """Return asset_utils.Asset based on args."""
+  return asset_utils.Asset(args.asset_name, _store(args))
+
+
 def add(args):
   """Add a new asset."""
-  asset_utils.Asset.add(args.asset_name,
-                        asset_utils.MultiStore(gsutil=args.gsutil))
+  asset_utils.Asset.add(args.asset_name, _store(args))
 
 
 def remove(args):
   """Remove an asset."""
-  asset_utils.Asset(args.asset_name,
-                    asset_utils.MultiStore(gsutil=args.gsutil)).remove()
+  _asset(args).remove()
 
 
 def download(args):
   """Download the current version of an asset."""
-  asset = asset_utils.Asset(args.asset_name,
-                            asset_utils.MultiStore(gsutil=args.gsutil))
-  asset.download_current_version(args.target_dir)
+  _asset(args).download_current_version(args.target_dir)
 
 
 def upload(args):
   """Upload a new version of the asset."""
-  asset = asset_utils.Asset(args.asset_name,
-                            asset_utils.MultiStore(gsutil=args.gsutil))
-  asset.upload_new_version(args.target_dir, commit=args.commit,
-                           extra_tags=args.extra_tags)
+  _asset(args).upload_new_version(args.target_dir, commit=args.commit,
+                                  extra_tags=args.extra_tags)
 
 
 def main(argv):
@@ -56,27 +68,23 @@
 
   prs_add = subs.add_parser('add', help='Add a new asset.')
   prs_add.set_defaults(func=add)
-  prs_add.add_argument('asset_name', help='Name of the asset.')
-  prs_add.add_argument('--gsutil')
+  _common_args(prs_add)
 
   prs_remove = subs.add_parser('remove', help='Remove an asset.')
   prs_remove.set_defaults(func=remove)
-  prs_remove.add_argument('asset_name', help='Name of the asset.')
-  prs_remove.add_argument('--gsutil')
+  _common_args(prs_remove)
 
   prs_download = subs.add_parser(
       'download', help='Download the current version of an asset.')
   prs_download.set_defaults(func=download)
-  prs_download.add_argument('asset_name', help='Name of the asset.')
+  _common_args(prs_download)
   prs_download.add_argument('--target_dir', '-t', required=True)
-  prs_download.add_argument('--gsutil')
 
   prs_upload = subs.add_parser(
       'upload', help='Upload a new version of an asset.')
   prs_upload.set_defaults(func=upload)
-  prs_upload.add_argument('asset_name', help='Name of the asset.')
+  _common_args(prs_upload)
   prs_upload.add_argument('--target_dir', '-t', required=True)
-  prs_upload.add_argument('--gsutil')
   prs_upload.add_argument('--commit', action='store_true')
   prs_upload.add_argument(
     '--extra_tags', nargs='+',
diff --git a/infra/bots/assets/mips64el_toolchain_linux/VERSION b/infra/bots/assets/mips64el_toolchain_linux/VERSION
deleted file mode 100644
index 62f9457..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-6
\ No newline at end of file
diff --git a/infra/bots/assets/mips64el_toolchain_linux/common.py b/infra/bots/assets/mips64el_toolchain_linux/common.py
deleted file mode 100755
index caa0ad8..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/common.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2017 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Common vars used by scripts in this directory."""
-
-
-import os
-import sys
-
-FILE_DIR = os.path.dirname(os.path.abspath(__file__))
-INFRA_BOTS_DIR = os.path.realpath(os.path.join(FILE_DIR, os.pardir, os.pardir))
-
-sys.path.insert(0, INFRA_BOTS_DIR)
-from assets import assets
-
-ASSET_NAME = os.path.basename(FILE_DIR)
-
-
-def run(cmd):
-  """Run a command, eg. "upload" or "download". """
-  assets.main([cmd, ASSET_NAME] + sys.argv[1:])
diff --git a/infra/bots/assets/mips64el_toolchain_linux/create.py b/infra/bots/assets/mips64el_toolchain_linux/create.py
deleted file mode 100755
index 5057129..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/create.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2018 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Create the asset."""
-
-
-import argparse
-import common
-import os
-import shutil
-import subprocess
-import utils
-
-# This is basically all the deps of g++-multilib-mips64el-linux-gnuabi64 that
-# are not already installed on the bots.
-#
-# We could try to also include packages that *are* already installed on the bots
-# as well, but that would be quite a bit, and would probably entail more hacky
-# fixes like below.
-#
-# There is probably a way to generate this list from apt, but it's not as
-# straightforward as it should be.
-PKGS = [
-    'binutils-mips64el-linux-gnuabi64',
-    'cpp-8-mips64el-linux-gnuabi64',
-    'g++-8-mips64el-linux-gnuabi64',
-    'gcc-8-cross-base',
-    'gcc-8-mips64el-linux-gnuabi64',
-    'gcc-8-mips64el-linux-gnuabi64-base',
-    'libatomic1-mips64el-cross',
-    'libc6-dev-mips64el-cross',
-    'libc6-mips64el-cross',
-    'libgcc-8-dev-mips64el-cross',
-    'libgcc1-mips64el-cross',
-    'libgomp1-mips64el-cross',
-    'libisl19',
-    'libmpfr6',  # This is new in buster, so build machines don't have it yet.
-    'libstdc++-8-dev-mips64el-cross',
-    'libstdc++6-mips64el-cross',
-    'linux-libc-dev-mips64el-cross',
-]
-
-def create_asset(target_dir):
-  """Create the asset."""
-  # This is all a bit hacky. Rather than installing to a chroot, we just extract
-  # all the packages to the target dir, then fix things up so that it can be
-  # used in our recipes.
-  with utils.tmp_dir():
-    # Download required Debian packages.
-    subprocess.check_call(['apt-get', 'download'] + PKGS)
-    for f in os.listdir('.'):
-      subprocess.check_call(['dpkg-deb', '--extract', f, target_dir])
-  parent_dir = os.path.join(target_dir, 'usr')
-  # Remove unnecessary files that cause problems with zipping (due to dangling
-  # symlinks).
-  os.remove(os.path.join(parent_dir,
-                         'lib/gcc-cross/mips64el-linux-gnuabi64/8/libcc1.so'))
-  shutil.rmtree(os.path.join(parent_dir, 'share'))
-  # Remove usr/ prefix.
-  for d in os.listdir(parent_dir):
-    os.rename(os.path.join(parent_dir, d), os.path.join(target_dir, d))
-  os.rmdir(parent_dir)
-  # Remove absolute paths in GNU ld scripts.
-  lib_dir = os.path.join(target_dir, 'mips64el-linux-gnuabi64/lib')
-  ld_script_token = 'OUTPUT_FORMAT(elf64-tradlittlemips)'
-  ld_script_files = subprocess.check_output(
-    ['grep', '--recursive', '--files-with-matches',
-     '--binary-files=without-match', '--fixed-strings', ld_script_token,
-     lib_dir]).split()
-  abs_path = '/usr/mips64el-linux-gnuabi64/lib/'
-  for f in ld_script_files:
-    with open(f) as script:
-      contents = script.read()
-    contents = contents.replace(abs_path, '')
-    with open(f, 'w') as script:
-      script.write(contents)
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--target_dir', '-t', required=True)
-  args = parser.parse_args()
-  create_asset(args.target_dir)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/infra/bots/assets/mips64el_toolchain_linux/create_and_upload.py b/infra/bots/assets/mips64el_toolchain_linux/create_and_upload.py
deleted file mode 100755
index de56a80..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/create_and_upload.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2017 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Create the asset and upload it."""
-
-
-import argparse
-import common
-import os
-import subprocess
-import sys
-import utils
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--gsutil')
-  args = parser.parse_args()
-
-  with utils.tmp_dir():
-    cwd = os.getcwd()
-    create_script = os.path.join(common.FILE_DIR, 'create.py')
-    upload_script = os.path.join(common.FILE_DIR, 'upload.py')
-
-    try:
-      subprocess.check_call(['python', create_script, '-t', cwd])
-      cmd = ['python', upload_script, '-t', cwd]
-      if args.gsutil:
-        cmd.extend(['--gsutil', args.gsutil])
-      subprocess.check_call(cmd)
-    except subprocess.CalledProcessError:
-      # Trap exceptions to avoid printing two stacktraces.
-      sys.exit(1)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/infra/bots/assets/mips64el_toolchain_linux/download.py b/infra/bots/assets/mips64el_toolchain_linux/download.py
deleted file mode 100755
index ca999e0..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/download.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2017 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Download the current version of the asset."""
-
-
-import common
-
-
-if __name__ == '__main__':
-  common.run('download')
diff --git a/infra/bots/assets/mips64el_toolchain_linux/upload.py b/infra/bots/assets/mips64el_toolchain_linux/upload.py
deleted file mode 100755
index bdfbda7..0000000
--- a/infra/bots/assets/mips64el_toolchain_linux/upload.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2017 Google Inc.
-#
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-"""Upload a new version of the asset."""
-
-
-import common
-
-
-if __name__ == '__main__':
-  common.run('upload')
diff --git a/infra/bots/assets/skimage/VERSION b/infra/bots/assets/skimage/VERSION
index 72f523f..86ee83a 100644
--- a/infra/bots/assets/skimage/VERSION
+++ b/infra/bots/assets/skimage/VERSION
@@ -1 +1 @@
-39
\ No newline at end of file
+40
\ No newline at end of file
diff --git a/infra/bots/assets/skp/VERSION b/infra/bots/assets/skp/VERSION
index 55596b2..58e77f3 100644
--- a/infra/bots/assets/skp/VERSION
+++ b/infra/bots/assets/skp/VERSION
@@ -1 +1 @@
-210
\ No newline at end of file
+215
\ No newline at end of file
diff --git a/infra/bots/assets/valgrind/VERSION b/infra/bots/assets/valgrind/VERSION
index c793025..f11c82a 100644
--- a/infra/bots/assets/valgrind/VERSION
+++ b/infra/bots/assets/valgrind/VERSION
@@ -1 +1 @@
-7
\ No newline at end of file
+9
\ No newline at end of file
diff --git a/infra/bots/assets/valgrind/create.py b/infra/bots/assets/valgrind/create.py
index 3f6707d..e01cc44 100755
--- a/infra/bots/assets/valgrind/create.py
+++ b/infra/bots/assets/valgrind/create.py
@@ -22,7 +22,7 @@
 import utils
 
 
-VALGRIND = 'valgrind-3.13.0'
+VALGRIND = 'valgrind-3.15.0'
 TARBALL = '%s.tar.bz2' % VALGRIND
 DOWNLOAD_URL = 'ftp://sourceware.org/pub/valgrind/%s' % TARBALL
 TEMP_DIR = os.path.join(tempfile.gettempdir(), 'skia-%s' % VALGRIND)
diff --git a/infra/bots/canvaskit.isolate b/infra/bots/canvaskit.isolate
new file mode 100644
index 0000000..5863ff9
--- /dev/null
+++ b/infra/bots/canvaskit.isolate
@@ -0,0 +1,13 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../modules/canvaskit',
+      '../../modules/pathkit/perf/perfReporter.js',
+      '../../modules/pathkit/tests/testReporter.js',
+      '../canvaskit',
+    ],
+  },
+}
diff --git a/infra/bots/check_deps.py b/infra/bots/check_deps.py
old mode 100644
new mode 100755
diff --git a/infra/bots/compile.isolate b/infra/bots/compile.isolate
index 85accb8..8b9f55a 100644
--- a/infra/bots/compile.isolate
+++ b/infra/bots/compile.isolate
@@ -12,6 +12,7 @@
       '../../bench',
       '../../bin/fetch-clang-format',
       '../../bin/fetch-gn',
+      '../../build/fuchsia',
       '../../build_overrides/dawn.gni',
       '../../buildtools',
       '../../dm',
@@ -43,7 +44,6 @@
       'assets/cast_toolchain/VERSION',
       'assets/clang_linux/VERSION',
       'assets/clang_win/VERSION',
-      'assets/mips64el_toolchain_linux/VERSION',
     ],
   },
 }
diff --git a/infra/bots/g3_compile/trigger_wait_g3_task.py b/infra/bots/g3_compile/trigger_wait_g3_task.py
old mode 100644
new mode 100755
index df0e49c..c796200
--- a/infra/bots/g3_compile/trigger_wait_g3_task.py
+++ b/infra/bots/g3_compile/trigger_wait_g3_task.py
@@ -16,6 +16,7 @@
 INFRA_BOTS_DIR = os.path.abspath(os.path.realpath(os.path.join(
     os.path.dirname(os.path.abspath(__file__)), os.pardir)))
 sys.path.insert(0, INFRA_BOTS_DIR)
+import git_utils
 import utils
 
 
@@ -108,6 +109,15 @@
   return task
 
 
+def _add_cl_comment(issue, comment):
+  # Depot tools needs a checkout to use "git cl" even though we are just adding
+  # a comment to a change unrelated to the checkout.
+  # TODO(rmistry): Try using the Gerrit API?
+  with git_utils.NewGitCheckout(repository=utils.SKIA_REPO):
+    add_comment_cmd = ['git', 'cl', 'comments', '-i', str(issue), '-a', comment]
+    subprocess.check_call(add_comment_cmd)
+
+
 def _read_from_storage(gs_file, use_expo_retries=True):
   """Returns the contents of the specified file from storage."""
   num_retries = GS_RETRIES if use_expo_retries else 1
@@ -168,6 +178,14 @@
       elif ret['status'] == 'merge_conflict':
           raise G3CompileException(MERGE_CONFLICT_ERROR_MSG)
       elif ret['status'] == 'failure':
+        # Add a comment to the CL before throwing an exception. See skbug/9631
+        # for context.
+        msg = ('FYI: The %s experimental CQ bot failed for patchset #%s.\n'
+               'The bot is known to be flaky and the failure might or might '
+               'not be related to this change.\n'
+               'Please take a quick look at http://cl/%s to verify.') % (
+                   options.builder_name, task['patchset'], ret['cl'])
+        _add_cl_comment(task['issue'], msg)
         raise G3CompileException(
             '\n\nRun failed G3 TAP: cl/%s' % ret['cl'] + PATCHING_INFORMATION)
       elif ret['status'] == 'success':
@@ -201,6 +219,9 @@
   option_parser.add_option(
       '', '--output_file', type=str,
       help='The file to write the task to.')
+  option_parser.add_option(
+      '', '--builder_name', type=str, default='',
+      help='The builder that triggered this run.')
   options, _ = option_parser.parse_args()
   sys.exit(trigger_and_wait(options))
 
diff --git a/infra/bots/gen_compile_isolate.py b/infra/bots/gen_compile_isolate.py
old mode 100644
new mode 100755
index 01f76ed..c9bf262
--- a/infra/bots/gen_compile_isolate.py
+++ b/infra/bots/gen_compile_isolate.py
@@ -40,7 +40,6 @@
   'infra/bots/assets/cast_toolchain/VERSION',
   'infra/bots/assets/clang_linux/VERSION',
   'infra/bots/assets/clang_win/VERSION',
-  'infra/bots/assets/mips64el_toolchain_linux/VERSION',
   'infra/canvaskit',
   'infra/pathkit',
   'resources',
diff --git a/infra/bots/gen_tasks_logic/gen_tasks_logic.go b/infra/bots/gen_tasks_logic/gen_tasks_logic.go
index 47b5e40..83fa034 100644
--- a/infra/bots/gen_tasks_logic/gen_tasks_logic.go
+++ b/infra/bots/gen_tasks_logic/gen_tasks_logic.go
@@ -22,8 +22,6 @@
 	"time"
 
 	"github.com/golang/glog"
-	"go.skia.org/infra/go/sklog"
-	"go.skia.org/infra/go/util"
 	"go.skia.org/infra/task_scheduler/go/specs"
 )
 
@@ -99,66 +97,10 @@
 			Path: "cache/docker",
 		},
 	}
-	// Versions of the following copied from
-	// https://chrome-internal.googlesource.com/infradata/config/+/master/configs/cr-buildbucket/swarming_task_template_canary.json#42
-	// to test the fix for chromium:836196.
-	// (In the future we may want to use versions from
-	// https://chrome-internal.googlesource.com/infradata/config/+/master/configs/cr-buildbucket/swarming_task_template.json#42)
-	// TODO(borenet): Roll these versions automatically!
-	CIPD_PKGS_PYTHON = []*specs.CipdPackage{
-		&specs.CipdPackage{
-			Name:    "infra/tools/luci/vpython/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "git_revision:f96db4b66034c859090be3c47eb38227277f228b",
-		},
-	}
 
-	CIPD_PKGS_CPYTHON = []*specs.CipdPackage{
-		&specs.CipdPackage{
-			Name:    "infra/python/cpython/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "version:2.7.15.chromium14",
-		},
-	}
-
-	CIPD_PKGS_KITCHEN = append([]*specs.CipdPackage{
-		&specs.CipdPackage{
-			Name:    "infra/tools/luci/kitchen/${platform}",
-			Path:    ".",
-			Version: "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc",
-		},
-		&specs.CipdPackage{
-			Name:    "infra/tools/luci-auth/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e",
-		},
-	}, CIPD_PKGS_PYTHON...)
-
-	CIPD_PKGS_GIT = []*specs.CipdPackage{
-		&specs.CipdPackage{
-			Name:    "infra/git/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "version:2.17.1.chromium15",
-		},
-		&specs.CipdPackage{
-			Name:    "infra/tools/git/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77",
-		},
-		&specs.CipdPackage{
-			Name:    "infra/tools/luci/git-credential-luci/${platform}",
-			Path:    "cipd_bin_packages",
-			Version: "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e",
-		},
-	}
-
-	CIPD_PKGS_GSUTIL = []*specs.CipdPackage{
-		&specs.CipdPackage{
-			Name:    "infra/gsutil",
-			Path:    "cipd_bin_packages",
-			Version: "version:4.28",
-		},
-	}
+	// TODO(borenet): This hacky and bad.
+	CIPD_PKGS_KITCHEN = append(specs.CIPD_PKGS_KITCHEN[:2], specs.CIPD_PKGS_PYTHON[1])
+	CIPD_PKG_CPYTHON  = specs.CIPD_PKGS_PYTHON[0]
 
 	CIPD_PKGS_XCODE = []*specs.CipdPackage{
 		// https://chromium.googlesource.com/chromium/tools/build/+/e19b7d9390e2bb438b566515b141ed2b9ed2c7c2/scripts/slave/recipe_modules/ios/api.py#317
@@ -246,9 +188,6 @@
 	// URL of the Skia Gold known hashes endpoint.
 	GoldHashesURL string `json:"gold_hashes_url"`
 
-	// GCS bucket used for Calmbench results.
-	GsBucketCalm string `json:"gs_bucket_calm"`
-
 	// GCS bucket used for GM results.
 	GsBucketGm string `json:"gs_bucket_gm"`
 
@@ -268,13 +207,12 @@
 	Project string `json:"project"`
 
 	// Service accounts.
-	ServiceAccountCompile         string `json:"service_account_compile"`
-	ServiceAccountHousekeeper     string `json:"service_account_housekeeper"`
-	ServiceAccountRecreateSKPs    string `json:"service_account_recreate_skps"`
-	ServiceAccountUploadBinary    string `json:"service_account_upload_binary"`
-	ServiceAccountUploadCalmbench string `json:"service_account_upload_calmbench"`
-	ServiceAccountUploadGM        string `json:"service_account_upload_gm"`
-	ServiceAccountUploadNano      string `json:"service_account_upload_nano"`
+	ServiceAccountCompile      string `json:"service_account_compile"`
+	ServiceAccountHousekeeper  string `json:"service_account_housekeeper"`
+	ServiceAccountRecreateSKPs string `json:"service_account_recreate_skps"`
+	ServiceAccountUploadBinary string `json:"service_account_upload_binary"`
+	ServiceAccountUploadGM     string `json:"service_account_upload_gm"`
+	ServiceAccountUploadNano   string `json:"service_account_upload_nano"`
 
 	// Optional override function which derives Swarming bot dimensions
 	// from parts of task names.
@@ -312,6 +250,18 @@
 	}
 }
 
+// In returns true if |s| is *in* |a| slice.
+// TODO(borenet): This is copied from go.skia.org/infra/go/util to avoid the
+// huge set of additional dependencies added by that package.
+func In(s string, a []string) bool {
+	for _, x := range a {
+		if x == s {
+			return true
+		}
+	}
+	return false
+}
+
 // GenTasks regenerates the tasks.json file. Loads the job list from a jobs.json
 // file which is the sibling of the calling gen_tasks.go file. If cfg is nil, it
 // is similarly loaded from a cfg.json file which is the sibling of the calling
@@ -369,7 +319,7 @@
 func getThisDirName() string {
 	_, thisFileName, _, ok := runtime.Caller(0)
 	if !ok {
-		sklog.Fatal("Unable to find path to current file.")
+		glog.Fatal("Unable to find path to current file.")
 	}
 	return filepath.Dir(filepath.Dir(thisFileName))
 }
@@ -381,7 +331,7 @@
 func getCallingDirName() string {
 	_, callingFileName, _, ok := runtime.Caller(2)
 	if !ok {
-		sklog.Fatal("Unable to find path to calling file.")
+		glog.Fatal("Unable to find path to calling file.")
 	}
 	return filepath.Dir(callingFileName)
 }
@@ -417,7 +367,7 @@
 
 	j, err := json.Marshal(d)
 	if err != nil {
-		sklog.Fatal(err)
+		glog.Fatal(err)
 	}
 	return strings.Replace(string(j), "\\u003c", "<", -1)
 }
@@ -427,9 +377,9 @@
 func (b *builder) kitchenTask(name, recipe, isolate, serviceAccount string, dimensions []string, extraProps map[string]string, outputDir string) *specs.TaskSpec {
 	cipd := append([]*specs.CipdPackage{}, CIPD_PKGS_KITCHEN...)
 	if strings.Contains(name, "Win") && !strings.Contains(name, "LenovoYogaC630") {
-		cipd = append(cipd, CIPD_PKGS_CPYTHON...)
+		cipd = append(cipd, CIPD_PKG_CPYTHON)
 	} else if strings.Contains(name, "P30") {
-		cipd = append(cipd, CIPD_PKGS_CPYTHON...)
+		cipd = append(cipd, CIPD_PKG_CPYTHON)
 	}
 	properties := map[string]string{
 		"buildername":   name,
@@ -503,7 +453,7 @@
 // deriveCompileTaskName returns the name of a compile task based on the given
 // job name.
 func (b *builder) deriveCompileTaskName(jobName string, parts map[string]string) string {
-	if parts["role"] == "Test" || parts["role"] == "Perf" || parts["role"] == "Calmbench" {
+	if parts["role"] == "Test" || parts["role"] == "Perf" {
 		task_os := parts["os"]
 		ec := []string{}
 		if val := parts["extra_config"]; val != "" {
@@ -512,17 +462,17 @@
 				"Skpbench", "AbandonGpuContext", "PreAbandonGpuContext", "Valgrind",
 				"ReleaseAndAbandonGpuContext", "CCPR", "FSAA", "FAAA", "FDAA", "NativeFonts", "GDI",
 				"NoGPUThreads", "ProcDump", "DDL1", "DDL3", "T8888", "DDLTotal", "DDLRecord", "9x9",
-				"BonusConfigs", "SkottieTracing", "SkottieWASM", "NonNVPR", "Mskp"}
+				"BonusConfigs", "SkottieTracing", "SkottieWASM", "NonNVPR", "Mskp", "Docker"}
 			keep := make([]string, 0, len(ec))
 			for _, part := range ec {
-				if !util.In(part, ignore) {
+				if !In(part, ignore) {
 					keep = append(keep, part)
 				}
 			}
 			ec = keep
 		}
 		if task_os == "Android" {
-			if !util.In("Android", ec) {
+			if !In("Android", ec) {
 				ec = append([]string{"Android"}, ec...)
 			}
 			task_os = "Debian9"
@@ -537,6 +487,10 @@
 			task_os = "Mac"
 		} else if strings.Contains(task_os, "Win") {
 			task_os = "Win"
+		} else if parts["compiler"] == "GCC" {
+			// GCC compiles are now on a Docker container. We use the same OS and
+			// version to compile as to test.
+			ec = append(ec, "Docker")
 		} else if strings.Contains(task_os, "Ubuntu") || strings.Contains(task_os, "Debian") {
 			task_os = "Debian9"
 		} else if strings.Contains(task_os, "Mac") {
@@ -591,6 +545,9 @@
 	d := map[string]string{
 		"pool": b.cfg.Pool,
 	}
+	if strings.Contains(parts["extra_config"], "Docker") && (parts["role"] == "Build" || (parts["cpu_or_gpu"] == "CPU" && parts["model"] == "GCE")) {
+		return b.dockerGceDimensions()
+	}
 	if os, ok := parts["os"]; ok {
 		d["os"], ok = map[string]string{
 			"Android":    "Android",
@@ -600,6 +557,7 @@
 			"Mac":        DEFAULT_OS_MAC,
 			"Mac10.13":   "Mac-10.13.6",
 			"Mac10.14":   "Mac-10.14.3",
+			"Mac10.15":   "Mac-10.15.1",
 			"Ubuntu18":   "Ubuntu-18.04",
 			"Win":        DEFAULT_OS_WIN,
 			"Win10":      "Windows-10-18363",
@@ -630,7 +588,7 @@
 	} else {
 		d["os"] = DEFAULT_OS_DEBIAN
 	}
-	if parts["role"] == "Test" || parts["role"] == "Perf" || parts["role"] == "Calmbench" {
+	if parts["role"] == "Test" || parts["role"] == "Perf" {
 		if strings.Contains(parts["os"], "Android") || strings.Contains(parts["os"], "Chromecast") {
 			// For Android, the device type is a better dimension
 			// than CPU or GPU.
@@ -650,6 +608,7 @@
 				"Pixel2XL":        {"taimen", "PPR1.180610.009"},
 				"Pixel3":          {"blueline", "PQ1A.190105.004"},
 				"Pixel3a":         {"sargo", "QP1A.190711.020"},
+				"Pixel4":          {"flame", "QD1A.190821.011.C4"},
 				"TecnoSpark3Pro":  {"TECNO-KB8", "PPR1.180610.011"},
 			}[parts["model"]]
 			if !ok {
@@ -828,15 +787,15 @@
 	target := filepath.Join(b.relpathTargetDir, f)
 	rv, err := filepath.Rel(b.relpathBaseDir, target)
 	if err != nil {
-		sklog.Fatal(err)
+		glog.Fatal(err)
 	}
 	return rv
 }
 
 // bundleRecipes generates the task to bundle and isolate the recipes.
 func (b *builder) bundleRecipes() string {
-	pkgs := append([]*specs.CipdPackage{}, CIPD_PKGS_GIT...)
-	pkgs = append(pkgs, CIPD_PKGS_PYTHON...)
+	pkgs := append([]*specs.CipdPackage{}, specs.CIPD_PKGS_GIT...)
+	pkgs = append(pkgs, specs.CIPD_PKGS_PYTHON...)
 	b.MustAddTask(BUNDLE_RECIPES_NAME, &specs.TaskSpec{
 		CipdPackages: pkgs,
 		Command: []string{
@@ -857,7 +816,7 @@
 func (b *builder) buildTaskDrivers() string {
 	b.MustAddTask(BUILD_TASK_DRIVERS_NAME, &specs.TaskSpec{
 		Caches:       CACHES_GO,
-		CipdPackages: append(CIPD_PKGS_GIT, b.MustGetCipdPackageFromAsset("go")),
+		CipdPackages: append(specs.CIPD_PKGS_GIT, b.MustGetCipdPackageFromAsset("go")),
 		Command: []string{
 			"/bin/bash", "skia/infra/bots/build_task_drivers.sh", specs.PLACEHOLDER_ISOLATED_OUTDIR,
 		},
@@ -873,7 +832,7 @@
 
 // updateGoDeps generates the task to update Go dependencies.
 func (b *builder) updateGoDeps(name string) string {
-	cipd := append([]*specs.CipdPackage{}, CIPD_PKGS_GIT...)
+	cipd := append([]*specs.CipdPackage{}, specs.CIPD_PKGS_GIT...)
 	cipd = append(cipd, b.MustGetCipdPackageFromAsset("go"))
 	cipd = append(cipd, b.MustGetCipdPackageFromAsset("protoc"))
 
@@ -947,7 +906,7 @@
 		// Skpbench only needs skps
 		deps = append(deps, ISOLATE_SKP_NAME)
 		deps = append(deps, ISOLATE_MSKP_NAME)
-	} else if util.In(o, rpiOS) {
+	} else if In(o, rpiOS) {
 		deps = append(deps, ISOLATE_SKP_NAME)
 		deps = append(deps, ISOLATE_SVG_NAME)
 		deps = append(deps, ISOLATE_SKIMAGE_NAME)
@@ -962,7 +921,7 @@
 	if !strings.Contains(name, "NoDEPS") {
 		t.Caches = append(t.Caches, CACHES_WORKDIR...)
 	}
-	t.CipdPackages = append(t.CipdPackages, CIPD_PKGS_GIT...)
+	t.CipdPackages = append(t.CipdPackages, specs.CIPD_PKGS_GIT...)
 }
 
 // usesGo adds attributes to tasks which use go. Recipes should use
@@ -979,7 +938,7 @@
 
 // usesDocker adds attributes to tasks which use docker.
 func usesDocker(t *specs.TaskSpec, name string) {
-	if strings.Contains(name, "EMCC") || strings.Contains(name, "SKQP") || strings.Contains(name, "LottieWeb") || strings.Contains(name, "CMake") {
+	if strings.Contains(name, "EMCC") || strings.Contains(name, "SKQP") || strings.Contains(name, "LottieWeb") || strings.Contains(name, "CMake") || strings.Contains(name, "Docker") {
 		t.Caches = append(t.Caches, CACHES_DOCKER...)
 	}
 }
@@ -1019,7 +978,6 @@
 		strings.Contains(name, "CMake") ||
 		strings.Contains(name, "CommandBuffer") ||
 		strings.Contains(name, "Flutter") ||
-		strings.Contains(name, "ParentRevision") ||
 		strings.Contains(name, "SKQP") {
 		recipe = "sync_and_compile"
 		isolate = "swarm_recipe.isolate"
@@ -1060,12 +1018,6 @@
 		if strings.Contains(name, "Clang") {
 			task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("clang_linux"))
 		}
-		if parts["target_arch"] == "mips64el" || parts["target_arch"] == "loongson3a" {
-			if parts["compiler"] != "GCC" {
-				glog.Fatalf("mips64el toolchain is GCC, but compiler is %q in %q", parts["compiler"], name)
-			}
-			task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("mips64el_toolchain_linux"))
-		}
 		if strings.Contains(name, "SwiftShader") {
 			task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("cmake_linux"))
 		}
@@ -1107,7 +1059,7 @@
 
 	// All compile tasks are runnable as their own Job. Assert that the Job
 	// is listed in jobs.
-	if !util.In(name, b.jobs) {
+	if !In(name, b.jobs) {
 		glog.Fatalf("Job %q is missing from the jobs list!", name)
 	}
 
@@ -1123,7 +1075,7 @@
 		fmt.Sprintf("os:%s", DEFAULT_OS_LINUX_GCE),
 	}
 	task := b.kitchenTask(name, "recreate_skps", "swarm_recipe.isolate", b.cfg.ServiceAccountRecreateSKPs, dims, EXTRA_PROPS, OUTPUT_NONE)
-	task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GIT...)
+	b.usesGit(task, name)
 	b.usesGo(task, name)
 	timeout(task, 4*time.Hour)
 	b.MustAddTask(name, task)
@@ -1134,8 +1086,9 @@
 // by hand.
 func (b *builder) checkGeneratedFiles(name string) string {
 	task := b.kitchenTask(name, "check_generated_files", "swarm_recipe.isolate", b.cfg.ServiceAccountCompile, b.linuxGceDimensions(MACHINE_TYPE_LARGE), EXTRA_PROPS, OUTPUT_NONE)
-	task.Caches = append(task.Caches, CACHES_WORKDIR...)
+	b.usesGit(task, name)
 	b.usesGo(task, name)
+	task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("clang_linux"))
 	b.MustAddTask(name, task)
 	return name
 }
@@ -1153,8 +1106,9 @@
 // the name of the last task in the generated chain of tasks, which the Job
 // should add as a dependency.
 func (b *builder) androidFrameworkCompile(name string) string {
-	task := b.kitchenTask(name, "android_compile", "compile_android_framework.isolate", b.cfg.ServiceAccountCompile, b.linuxGceDimensions(MACHINE_TYPE_SMALL), EXTRA_PROPS, OUTPUT_NONE)
+	task := b.kitchenTask(name, "android_compile", "compile_android_framework.isolate", "skia-android-framework-compile@skia-swarming-bots.iam.gserviceaccount.com", b.linuxGceDimensions(MACHINE_TYPE_SMALL), EXTRA_PROPS, OUTPUT_NONE)
 	timeout(task, 2*time.Hour)
+	b.usesGit(task, name)
 	b.MustAddTask(name, task)
 	return name
 }
@@ -1163,8 +1117,9 @@
 // the name of the last task in the generated chain of tasks, which the Job
 // should add as a dependency.
 func (b *builder) g3FrameworkCompile(name string) string {
-	task := b.kitchenTask(name, "g3_compile", "compile_g3_framework.isolate", b.cfg.ServiceAccountCompile, b.linuxGceDimensions(MACHINE_TYPE_SMALL), EXTRA_PROPS, OUTPUT_NONE)
+	task := b.kitchenTask(name, "g3_compile", "compile_g3_framework.isolate", "skia-g3-framework-compile@skia-swarming-bots.iam.gserviceaccount.com", b.linuxGceDimensions(MACHINE_TYPE_SMALL), EXTRA_PROPS, OUTPUT_NONE)
 	timeout(task, 3*time.Hour)
+	b.usesGit(task, name)
 	b.MustAddTask(name, task)
 	return name
 }
@@ -1187,7 +1142,7 @@
 		"repository": specs.PLACEHOLDER_REPO,
 	}
 	task := b.kitchenTask(name, "infra", "infra_tests.isolate", b.cfg.ServiceAccountCompile, dims, extraProps, OUTPUT_NONE)
-	task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GSUTIL...)
+	task.CipdPackages = append(task.CipdPackages, specs.CIPD_PKGS_GSUTIL...)
 	task.Idempotent = true
 	// Repos which call into Skia's gen_tasks.go should define their own
 	// infra_tests.isolate and therefore should not use relpath().
@@ -1204,11 +1159,12 @@
 	task := b.kitchenTask(name, "compute_buildstats", "swarm_recipe.isolate", "", b.swarmDimensions(parts), EXTRA_PROPS, OUTPUT_PERF)
 	task.Dependencies = append(task.Dependencies, compileTaskName)
 	task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("bloaty"))
+	b.usesGit(task, name)
 	b.MustAddTask(name, task)
 
 	// Upload release results (for tracking in perf)
 	// We have some jobs that are FYI (e.g. Debug-CanvasKit, tree-map generator)
-	if strings.Contains(name, "Release") && !util.In(name, BUILD_STATS_NO_UPLOAD) {
+	if strings.Contains(name, "Release") && !In(name, BUILD_STATS_NO_UPLOAD) {
 		uploadName := fmt.Sprintf("%s%s%s", PREFIX_UPLOAD, b.jobNameSchema.Sep, name)
 		extraProps := map[string]string{
 			"gs_bucket": b.cfg.GsBucketNano,
@@ -1217,44 +1173,7 @@
 			extraProps[k] = v
 		}
 		uploadTask := b.kitchenTask(name, "upload_buildstats_results", "swarm_recipe.isolate", b.cfg.ServiceAccountUploadNano, b.linuxGceDimensions(MACHINE_TYPE_SMALL), extraProps, OUTPUT_NONE)
-		uploadTask.CipdPackages = append(uploadTask.CipdPackages, CIPD_PKGS_GSUTIL...)
-		uploadTask.Dependencies = append(uploadTask.Dependencies, name)
-		b.MustAddTask(uploadName, uploadTask)
-		return uploadName
-	}
-
-	return name
-}
-
-// getParentRevisionName returns the name of a compile task which builds
-// against a "parent" revision.
-func getParentRevisionName(compileTaskName string, parts map[string]string) string {
-	if parts["extra_config"] == "" {
-		return compileTaskName + "-ParentRevision"
-	} else {
-		return compileTaskName + "_ParentRevision"
-	}
-}
-
-// calmbench generates a calmbench task. Returns the name of the last task in the
-// generated chain of tasks, which the Job should add as a dependency.
-func (b *builder) calmbench(name string, parts map[string]string, compileTaskName, compileParentName string) string {
-	task := b.kitchenTask(name, "calmbench", "calmbench.isolate", "", b.swarmDimensions(parts), EXTRA_PROPS, OUTPUT_PERF)
-	b.usesGit(task, name)
-	task.Dependencies = append(task.Dependencies, compileTaskName, compileParentName, ISOLATE_SKP_NAME, ISOLATE_SVG_NAME)
-	b.MustAddTask(name, task)
-
-	// Upload results if necessary.
-	if strings.Contains(name, "Release") && b.doUpload(name) {
-		uploadName := fmt.Sprintf("%s%s%s", PREFIX_UPLOAD, b.jobNameSchema.Sep, name)
-		extraProps := map[string]string{
-			"gs_bucket": b.cfg.GsBucketCalm,
-		}
-		for k, v := range EXTRA_PROPS {
-			extraProps[k] = v
-		}
-		uploadTask := b.kitchenTask(name, "upload_calmbench_results", "swarm_recipe.isolate", b.cfg.ServiceAccountUploadCalmbench, b.linuxGceDimensions(MACHINE_TYPE_SMALL), extraProps, OUTPUT_NONE)
-		uploadTask.CipdPackages = append(uploadTask.CipdPackages, CIPD_PKGS_GSUTIL...)
+		uploadTask.CipdPackages = append(uploadTask.CipdPackages, specs.CIPD_PKGS_GSUTIL...)
 		uploadTask.Dependencies = append(uploadTask.Dependencies, name)
 		b.MustAddTask(uploadName, uploadTask)
 		return uploadName
@@ -1280,8 +1199,10 @@
 // test generates a Test task. Returns the name of the last task in the
 // generated chain of tasks, which the Job should add as a dependency.
 func (b *builder) test(name string, parts map[string]string, compileTaskName string, pkgs []*specs.CipdPackage) string {
+	isolate := "test_skia_bundled.isolate"
 	recipe := "test"
 	if strings.Contains(name, "SKQP") {
+		isolate = "skqp.isolate"
 		recipe = "skqp_test"
 		if strings.Contains(name, "Emulator") {
 			recipe = "test_skqp_emulator"
@@ -1291,10 +1212,13 @@
 		// running hs_bench or kx, it will be easier to fit into the current job name schema.
 		recipe = "compute_test"
 	} else if strings.Contains(name, "PathKit") {
+		isolate = "pathkit.isolate"
 		recipe = "test_pathkit"
 	} else if strings.Contains(name, "CanvasKit") {
+		isolate = "canvaskit.isolate"
 		recipe = "test_canvaskit"
 	} else if strings.Contains(name, "LottieWeb") {
+		isolate = "lottie_web.isolate"
 		recipe = "test_lottie_web"
 	}
 	extraProps := map[string]string{
@@ -1307,10 +1231,6 @@
 	if iid != nil {
 		extraProps["internal_hardware_label"] = strconv.Itoa(*iid)
 	}
-	isolate := "test_skia_bundled.isolate"
-	if strings.Contains(name, "CanvasKit") || strings.Contains(name, "Emulator") || strings.Contains(name, "LottieWeb") || strings.Contains(name, "PathKit") {
-		isolate = "swarm_recipe.isolate"
-	}
 	task := b.kitchenTask(name, recipe, isolate, "", b.swarmDimensions(parts), extraProps, OUTPUT_TEST)
 	task.CipdPackages = append(task.CipdPackages, pkgs...)
 	if strings.Contains(name, "Lottie") {
@@ -1361,7 +1281,7 @@
 			extraProps[k] = v
 		}
 		uploadTask := b.kitchenTask(name, "upload_dm_results", "swarm_recipe.isolate", b.cfg.ServiceAccountUploadGM, b.linuxGceDimensions(MACHINE_TYPE_SMALL), extraProps, OUTPUT_NONE)
-		uploadTask.CipdPackages = append(uploadTask.CipdPackages, CIPD_PKGS_GSUTIL...)
+		uploadTask.CipdPackages = append(uploadTask.CipdPackages, specs.CIPD_PKGS_GSUTIL...)
 		uploadTask.Dependencies = append(uploadTask.Dependencies, name)
 		b.MustAddTask(uploadName, uploadTask)
 		return uploadName
@@ -1379,13 +1299,19 @@
 		recipe = "skpbench"
 		isolate = b.relpath("skpbench_skia_bundled.isolate")
 	} else if strings.Contains(name, "PathKit") {
+		isolate = "pathkit.isolate"
 		recipe = "perf_pathkit"
 	} else if strings.Contains(name, "CanvasKit") {
+		isolate = "canvaskit.isolate"
 		recipe = "perf_canvaskit"
 	} else if strings.Contains(name, "SkottieTracing") {
 		recipe = "perf_skottietrace"
-	} else if strings.Contains(name, "SkottieWASM") || strings.Contains(name, "LottieWeb") {
+	} else if strings.Contains(name, "SkottieWASM") {
 		recipe = "perf_skottiewasm_lottieweb"
+		isolate = "skottie_wasm.isolate"
+	} else if strings.Contains(name, "LottieWeb") {
+		recipe = "perf_skottiewasm_lottieweb"
+		isolate = "lottie_web.isolate"
 	}
 	task := b.kitchenTask(name, recipe, isolate, "", b.swarmDimensions(parts), EXTRA_PROPS, OUTPUT_PERF)
 	task.CipdPackages = append(task.CipdPackages, pkgs...)
@@ -1414,10 +1340,14 @@
 	} else if strings.Contains(parts["extra_config"], "SkottieWASM") || strings.Contains(parts["extra_config"], "LottieWeb") {
 		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("node"))
 		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("lottie-samples"))
-		task.CipdPackages = append(task.CipdPackages, CIPD_PKGS_GIT...)
 	} else if strings.Contains(parts["extra_config"], "Skottie") {
 		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("lottie-samples"))
 	}
+
+	if strings.Contains(name, "Android") && strings.Contains(name, "CPU") {
+		task.CipdPackages = append(task.CipdPackages, b.MustGetCipdPackageFromAsset("text_blob_traces"))
+	}
+
 	iid := b.internalHardwareLabel(parts)
 	if iid != nil {
 		task.Command = append(task.Command, fmt.Sprintf("internal_hardware_label=%d", *iid))
@@ -1434,7 +1364,7 @@
 			extraProps[k] = v
 		}
 		uploadTask := b.kitchenTask(name, "upload_nano_results", "swarm_recipe.isolate", b.cfg.ServiceAccountUploadNano, b.linuxGceDimensions(MACHINE_TYPE_SMALL), extraProps, OUTPUT_NONE)
-		uploadTask.CipdPackages = append(uploadTask.CipdPackages, CIPD_PKGS_GSUTIL...)
+		uploadTask.CipdPackages = append(uploadTask.CipdPackages, specs.CIPD_PKGS_GSUTIL...)
 		uploadTask.Dependencies = append(uploadTask.Dependencies, name)
 		b.MustAddTask(uploadName, uploadTask)
 		return uploadName
@@ -1527,11 +1457,6 @@
 	if err != nil {
 		glog.Fatal(err)
 	}
-	compileParentName := getParentRevisionName(compileTaskName, compileTaskParts)
-	compileParentParts, err := b.jobNameSchema.ParseJobName(compileParentName)
-	if err != nil {
-		glog.Fatal(err)
-	}
 
 	// These bots do not need a compile task.
 	if parts["role"] != "Build" &&
@@ -1549,9 +1474,6 @@
 		!strings.Contains(name, "SkottieWASM") &&
 		!strings.Contains(name, "LottieWeb") {
 		b.compile(compileTaskName, compileTaskParts)
-		if parts["role"] == "Calmbench" {
-			b.compile(compileParentName, compileParentParts)
-		}
 	}
 
 	// Housekeepers.
@@ -1614,11 +1536,6 @@
 		deps = append(deps, b.perf(name, parts, compileTaskName, pkgs))
 	}
 
-	// Calmbench bots.
-	if parts["role"] == "Calmbench" {
-		deps = append(deps, b.calmbench(name, parts, compileTaskName, compileParentName))
-	}
-
 	// Valgrind runs at a low priority so that it doesn't occupy all the bots.
 	if strings.Contains(name, "Valgrind") {
 		// Priority of 0.085 should result in Valgrind tasks with a blamelist of ~10 commits having the
@@ -1675,7 +1592,11 @@
 	if err != nil {
 		return nil, err
 	}
-	defer util.Close(f)
+	defer func() {
+		if err := f.Close(); err != nil {
+			glog.Errorf("Failed to close %s: %s", jsonFile, err)
+		}
+	}()
 	if err := json.NewDecoder(f).Decode(&rv); err != nil {
 		return nil, err
 	}
diff --git a/infra/bots/git_utils.py b/infra/bots/git_utils.py
old mode 100644
new mode 100755
diff --git a/infra/bots/infra_tests.py b/infra/bots/infra_tests.py
index 10546ed..36da4921 100755
--- a/infra/bots/infra_tests.py
+++ b/infra/bots/infra_tests.py
@@ -52,11 +52,6 @@
   except OSError:
     return ('Failed to run "%s"; do you have Go installed on your machine?'
             % ' '.join(cmd))
-  if output:
-    if ('cannot find package "go.skia.org/infra' in output or
-        'gen_tasks.go:' in output):
-      return ('Failed to run gen_tests.go:\n\n%s\nMaybe you need to run:\n\n'
-              '$ go get -u go.skia.org/infra/...' % output)
   return output
 
 
diff --git a/infra/bots/jobs.json b/infra/bots/jobs.json
index c206e45..a3baa85 100644
--- a/infra/bots/jobs.json
+++ b/infra/bots/jobs.json
@@ -1,4 +1,16 @@
 [
+  "Build-Debian10-GCC-loongson3a-Debug-Docker",
+  "Build-Debian10-GCC-loongson3a-Release-Docker",
+  "Build-Debian10-GCC-mips64el-Debug-Docker",
+  "Build-Debian10-GCC-mips64el-Release-Docker",
+  "Build-Debian10-GCC-x86-Debug-Docker",
+  "Build-Debian10-GCC-x86-Release-Docker",
+  "Build-Debian10-GCC-x86_64-Debug-Docker",
+  "Build-Debian10-GCC-x86_64-Debug-NoGPU_Docker",
+  "Build-Debian10-GCC-x86_64-Release-Docker",
+  "Build-Debian10-GCC-x86_64-Release-NoGPU_Docker",
+  "Build-Debian10-GCC-x86_64-Release-Shared_Docker",
+  "Build-Debian9-Clang-TAP-Presubmit-G3_Framework",
   "Build-Debian9-Clang-arm-Debug-Android",
   "Build-Debian9-Clang-arm-Debug-Android_Vulkan",
   "Build-Debian9-Clang-arm-Debug-Chromebook_GLES",
@@ -17,6 +29,7 @@
   "Build-Debian9-Clang-arm64-Release-Android_ASAN",
   "Build-Debian9-Clang-arm64-Release-Android_ASAN_Vulkan",
   "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
+  "Build-Debian9-Clang-arm64-Release-Android_Wuffs",
   "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework",
   "Build-Debian9-Clang-host-sdk-Android_Framework",
   "Build-Debian9-Clang-x64-Debug-Android",
@@ -50,18 +63,17 @@
   "Build-Debian9-Clang-x86_64-Release-Chromebook_GLES",
   "Build-Debian9-Clang-x86_64-Release-Fast",
   "Build-Debian9-Clang-x86_64-Release-NoDEPS",
-  "Build-Debian9-Clang-x86_64-Release-ParentRevision",
   "Build-Debian9-Clang-x86_64-Release-SKNX_NO_SIMD",
   "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE2",
   "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41",
   "Build-Debian9-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER",
+  "Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER",
   "Build-Debian9-Clang-x86_64-Release-Static",
   "Build-Debian9-Clang-x86_64-Release-SwiftShader",
   "Build-Debian9-Clang-x86_64-Release-TSAN",
   "Build-Debian9-Clang-x86_64-Release-TSAN_Vulkan",
   "Build-Debian9-Clang-x86_64-Release-Vulkan",
   "Build-Debian9-Clang-x86_64-Release-Wuffs",
-  "Build-Debian9-Clang-TAP-Presubmit-G3_Framework",
   "Build-Debian9-EMCC-asmjs-Debug-PathKit",
   "Build-Debian9-EMCC-asmjs-Release-PathKit",
   "Build-Debian9-EMCC-wasm-Debug-CanvasKit",
@@ -70,18 +82,6 @@
   "Build-Debian9-EMCC-wasm-Release-CanvasKit",
   "Build-Debian9-EMCC-wasm-Release-CanvasKit_CPU",
   "Build-Debian9-EMCC-wasm-Release-PathKit",
-  "Build-Debian9-GCC-loongson3a-Debug",
-  "Build-Debian9-GCC-loongson3a-Release",
-  "Build-Debian9-GCC-mips64el-Debug",
-  "Build-Debian9-GCC-mips64el-Release",
-  "Build-Debian9-GCC-x86-Debug",
-  "Build-Debian9-GCC-x86-Release",
-  "Build-Debian9-GCC-x86_64-Debug",
-  "Build-Debian9-GCC-x86_64-Debug-NoGPU",
-  "Build-Debian9-GCC-x86_64-Release",
-  "Build-Debian9-GCC-x86_64-Release-NoGPU",
-  "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41",
-  "Build-Debian9-GCC-x86_64-Release-Shared",
   "Build-Mac-Clang-arm-Debug-iOS",
   "Build-Mac-Clang-arm-Release-iOS",
   "Build-Mac-Clang-arm64-Debug-Android",
@@ -141,8 +141,6 @@
   "BuildStats-Debian9-EMCC-wasm-Release-CanvasKit",
   "BuildStats-Debian9-EMCC-wasm-Release-CanvasKit_CPU",
   "BuildStats-Debian9-EMCC-wasm-Release-PathKit",
-  "Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All",
-  "Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All",
   "Housekeeper-Nightly-RecreateSKPs_Canary",
   "Housekeeper-Nightly-UpdateGoDeps",
   "Housekeeper-OnDemand-Presubmit",
@@ -152,9 +150,9 @@
   "Housekeeper-PerCommit-CheckGeneratedFiles",
   "Housekeeper-PerCommit-InfraTests_Linux",
   "Housekeeper-PerCommit-InfraTests_Win",
+  "Housekeeper-PerCommit-IsolateMSKP",
   "Housekeeper-PerCommit-IsolateSKP",
   "Housekeeper-PerCommit-IsolateSVG",
-  "Housekeeper-PerCommit-IsolateMSKP",
   "Housekeeper-PerCommit-IsolateSkImage",
   "Housekeeper-Weekly-RecreateSKPs",
   "Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android",
@@ -175,31 +173,25 @@
   "Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-All-Android_Vulkan_NoGPUThreads",
   "Perf-Android-Clang-Nexus5-CPU-Snapdragon800-arm-Release-All-Android",
   "Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android",
+  "Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs",
   "Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android",
   "Perf-Android-Clang-Nexus7-GPU-Tegra3-arm-Release-All-Android",
   "Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android",
   "Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android_Vulkan",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench",
-  "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Mskp_Skpbench",
+  "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Vulkan",
   "Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Vulkan_Skpbench",
   "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android",
-  "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench",
-  "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench",
   "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan",
-  "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench",
   "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android",
-  "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench",
-  "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench",
   "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan",
-  "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench",
   "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android",
-  "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench",
-  "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench",
   "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan",
-  "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench",
+  "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android",
+  "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan",
   "Perf-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Release-All-Android",
   "Perf-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Release-All",
   "Perf-ChromeOS-Clang-AcerChromebook13_CB5_311-GPU-TegraK1-arm-Release-All",
@@ -239,13 +231,13 @@
   "Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal",
   "Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All",
   "Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer",
-  "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All",
-  "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer",
+  "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All",
+  "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer",
   "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All",
   "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ASAN",
+  "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41",
   "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan",
   "Perf-Ubuntu18-EMCC-Golo-GPU-QuadroP400-wasm-Release-All-SkottieWASM",
-  "Perf-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41",
   "Perf-Ubuntu18-none-Golo-GPU-QuadroP400-x86_64-Release-All-LottieWeb",
   "Perf-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-All",
   "Perf-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Release-All-ANGLE",
@@ -362,6 +354,10 @@
   "Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan",
   "Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android",
   "Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan",
+  "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android",
+  "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan",
+  "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android",
+  "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan",
   "Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android",
   "Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Release-All-Android",
   "Test-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Debug-All",
@@ -380,6 +376,10 @@
   "Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All",
   "Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Debug-All",
   "Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All",
+  "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker",
+  "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker",
+  "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker",
+  "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86-Debug-All",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN",
@@ -398,6 +398,7 @@
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_CPU_LIMIT_SSE2",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_CPU_LIMIT_SSE41",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER",
+  "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER",
   "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN",
   "Test-Debian9-Clang-GCE-CPU-AVX512-x86_64-Debug-All",
   "Test-Debian9-Clang-GCE-CPU-AVX512-x86_64-Release-All",
@@ -433,10 +434,6 @@
   "Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit",
   "Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit",
   "Test-Debian9-EMCC-GCE-GPU-AVX2-wasm-Release-All-CanvasKit",
-  "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All",
-  "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All",
-  "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All",
-  "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All",
   "Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All",
   "Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-CommandBuffer",
   "Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-DDL1_Metal",
@@ -467,12 +464,13 @@
   "Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All",
   "Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-Metal",
   "Test-Mac10.13-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts",
-  "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All",
-  "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer",
-  "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal",
-  "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All",
-  "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal",
   "Test-Mac10.14-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All",
+  "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-ASAN",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1",
@@ -487,10 +485,10 @@
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-DDL3_TSAN",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-DDL3_TSAN_Vulkan",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-PreAbandonGpuContext",
+  "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41",
+  "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41",
+  "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41",
   "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan",
-  "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41",
-  "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41",
-  "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41",
   "Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All",
   "Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All-ANGLE",
   "Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All-Vulkan",
diff --git a/infra/bots/lottie_web.isolate b/infra/bots/lottie_web.isolate
new file mode 100644
index 0000000..eeb475e
--- /dev/null
+++ b/infra/bots/lottie_web.isolate
@@ -0,0 +1,10 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../tools/lottie-web-perf',
+    ],
+  },
+}
diff --git a/infra/bots/pathkit.isolate b/infra/bots/pathkit.isolate
new file mode 100644
index 0000000..b773253
--- /dev/null
+++ b/infra/bots/pathkit.isolate
@@ -0,0 +1,11 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../modules/pathkit',
+      '../pathkit',
+    ],
+  },
+}
diff --git a/infra/bots/recipe_modules/build/__init__.py b/infra/bots/recipe_modules/build/__init__.py
index a7325b4..8a8ba88 100644
--- a/infra/bots/recipe_modules/build/__init__.py
+++ b/infra/bots/recipe_modules/build/__init__.py
@@ -4,6 +4,7 @@
 
 DEPS = [
   'depot_tools/gclient',
+  'docker',
   'env',
   'infra',
   'recipe_engine/context',
diff --git a/infra/bots/recipe_modules/build/android.py b/infra/bots/recipe_modules/build/android.py
index d352eb3..7a990cf 100644
--- a/infra/bots/recipe_modules/build/android.py
+++ b/infra/bots/recipe_modules/build/android.py
@@ -46,6 +46,8 @@
     args['skia_enable_vulkan_debug_layers'] = 'false'
   if 'ASAN' in extra_tokens:
     args['sanitize'] = '"ASAN"'
+  if 'Wuffs' in extra_tokens:
+    args['skia_use_wuffs'] = 'true'
 
   # If an Android API level is specified, use that.
   for t in extra_tokens:
diff --git a/infra/bots/recipe_modules/build/api.py b/infra/bots/recipe_modules/build/api.py
index 0edf5c0..c4964d9 100644
--- a/infra/bots/recipe_modules/build/api.py
+++ b/infra/bots/recipe_modules/build/api.py
@@ -14,6 +14,7 @@
 from . import chromecast
 from . import cmake
 from . import default
+from . import docker
 from . import flutter
 from . import pathkit
 from . import skqp
@@ -47,6 +48,9 @@
     elif 'CMake' in b:
       self.compile_fn = cmake.compile_fn
       self.copy_fn = cmake.copy_build_products
+    elif 'Docker' in b:
+      self.compile_fn = docker.compile_fn
+      self.copy_fn = docker.copy_build_products
     else:
       self.compile_fn = default.compile_fn
       self.copy_fn = default.copy_build_products
diff --git a/infra/bots/recipe_modules/build/default.py b/infra/bots/recipe_modules/build/default.py
index 23430b4..b4a2b28 100644
--- a/infra/bots/recipe_modules/build/default.py
+++ b/infra/bots/recipe_modules/build/default.py
@@ -127,6 +127,11 @@
         # We have some bots on 10.13.
         env['MACOSX_DEPLOYMENT_TARGET'] = '10.13'
 
+  if 'CheckGeneratedFiles' in extra_tokens:
+    compiler = 'Clang'
+    args['skia_compile_processors'] = 'true'
+    args['skia_generate_workarounds'] = 'true'
+
   if compiler == 'Clang' and api.vars.is_linux:
     cc  = clang_linux + '/bin/clang'
     cxx = clang_linux + '/bin/clang++'
@@ -140,28 +145,6 @@
 
   elif compiler == 'Clang':
     cc, cxx = 'clang', 'clang++'
-  elif compiler == 'GCC':
-    if target_arch in ['mips64el', 'loongson3a']:
-      mips64el_toolchain_linux = str(api.vars.slave_dir.join(
-          'mips64el_toolchain_linux'))
-      cc  = mips64el_toolchain_linux + '/bin/mips64el-linux-gnuabi64-gcc-8'
-      cxx = mips64el_toolchain_linux + '/bin/mips64el-linux-gnuabi64-g++-8'
-      env['LD_LIBRARY_PATH'] = (
-          mips64el_toolchain_linux + '/lib/x86_64-linux-gnu/')
-      extra_ldflags.append('-L' + mips64el_toolchain_linux +
-                           '/mips64el-linux-gnuabi64/lib')
-      extra_cflags.extend([
-          ('-DDUMMY_mips64el_toolchain_linux_version=%s' %
-           api.run.asset_version('mips64el_toolchain_linux', skia_dir))
-      ])
-      args.update({
-        'skia_use_system_freetype2': 'false',
-        'skia_use_fontconfig':       'false',
-        'skia_enable_gpu':           'false',
-        'werror':                    'false',
-      })
-    else:
-      cc, cxx = 'gcc', 'g++'
 
   if 'Tidy' in extra_tokens:
     # Swap in clang-tidy.sh for clang++, but update PATH so it can find clang++.
@@ -242,8 +225,6 @@
       'skia_use_vulkan':        'false',
       'skia_use_zlib':          'false',
     })
-  if 'NoGPU' in extra_tokens:
-    args['skia_enable_gpu'] = 'false'
   if 'Shared' in extra_tokens:
     args['is_component_build'] = 'true'
   if 'Vulkan' in extra_tokens and not 'Android' in extra_tokens:
@@ -274,9 +255,6 @@
     args['skia_ios_profile'] = '"%s"' % api.vars.slave_dir.join(
         'provisioning_profile_ios',
         'Upstream_Testing_Provisioning_Profile.mobileprovision')
-  if 'CheckGeneratedFiles' in extra_tokens:
-    args['skia_compile_processors'] = 'true'
-    args['skia_generate_workarounds'] = 'true'
   if compiler == 'Clang' and 'Win' in os:
     args['clang_win'] = '"%s"' % api.vars.slave_dir.join('clang_win')
     extra_cflags.append('-DDUMMY_clang_win_version=%s' %
diff --git a/infra/bots/recipe_modules/build/docker.py b/infra/bots/recipe_modules/build/docker.py
new file mode 100644
index 0000000..09aa329
--- /dev/null
+++ b/infra/bots/recipe_modules/build/docker.py
@@ -0,0 +1,92 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+from . import util
+
+
+# TODO(dogben): Move this mapping to a machine-editable file.
+IMAGES = {
+    'gcc-debian10': (
+        'gcr.io/skia-public/gcc-debian10@sha256:'
+        '89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c'),
+    'gcc-debian10-x86': (
+        'gcr.io/skia-public/gcc-debian10-x86@sha256:'
+        'b1ec55403ac66d9500d033d6ffd7663894d32335711fbbb0fb4c67dfce812203'),
+    'gcc-debian10-mips64el': (
+        'gcr.io/skia-public/gcc-debian10-mips64el@sha256:'
+        'c173a718d9f62f0cd1e5335713ebc4721d5dcf662fb02597744b71c53338a540'),
+}
+
+
+def py_to_gn(val):
+  """Convert val to a string that can be used as GN args."""
+  if isinstance(val, bool):
+    return 'true' if val else 'false'
+  elif isinstance(val, basestring):
+    # TODO(dogben): Handle quoting "$\
+    return '"%s"' % val
+  elif isinstance(val, (list, tuple)):
+    return '[%s]' % (','.join(py_to_gn(x) for x in val))
+  elif isinstance(val, dict):
+    gn = ' '.join(
+        '%s=%s' % (k, py_to_gn(v)) for (k, v) in sorted(val.iteritems()))
+    return gn
+  else:  # pragma: nocover
+    raise Exception('Converting %s to gn is not implemented.' % type(val))
+
+
+def compile_fn(api, checkout_root, out_dir):
+  compiler = api.vars.builder_cfg.get('compiler', '')
+  configuration = api.vars.builder_cfg.get('configuration', '')
+  extra_tokens = api.vars.extra_tokens
+  extra_tokens.remove('Docker')
+  os = api.vars.builder_cfg.get('os', '')
+  target_arch = api.vars.builder_cfg.get('target_arch', '')
+
+  args = {
+      'extra_cflags': [],
+      'extra_ldflags': [],
+      'target_cpu': target_arch,
+      'werror': True
+  }
+  if configuration != 'Debug':
+    args['is_debug'] = False
+
+  if 'NoGPU' in extra_tokens:
+    args['skia_enable_gpu'] = False
+    extra_tokens.remove('NoGPU')
+  if 'Shared' in extra_tokens:
+    args['is_component_build'] = True
+    extra_tokens.remove('Shared')
+
+  image_name = None
+  if os == 'Debian10' and compiler == 'GCC' and not extra_tokens:
+    args['cc'] = 'gcc'
+    args['cxx'] = 'g++'
+    if target_arch == 'x86_64':
+      image_name = 'gcc-debian10'
+    elif target_arch == 'x86':
+      image_name = 'gcc-debian10-x86'
+    elif target_arch in ['mips64el', 'loongson3a']:
+      image_name = 'gcc-debian10-mips64el'
+      args['cc'] = '/usr/bin/mips64el-linux-gnuabi64-gcc-8'
+      args['cxx'] = '/usr/bin/mips64el-linux-gnuabi64-g++-8'
+
+  if not image_name:
+    raise Exception('Not implemented: ' + api.vars.builder_name)
+
+  image_hash = IMAGES[image_name]
+  # We always perform an incremental compile, since out dir is cached across
+  # compile tasks. However, we need to force a recompile when the toolchain
+  # changes. The simplest way to do that is using a C define that changes
+  # anytime the image changes.
+  args['extra_cflags'].append('-DDUMMY_docker_image=%s' % image_hash)
+
+  script = api.build.resource('docker-compile.sh')
+  api.docker.run('Run build script in Docker', image_hash,
+                 checkout_root, out_dir, script, args=[py_to_gn(args)])
+
+def copy_build_products(api, src, dst):
+  util.copy_listed_files(api, src, dst, util.DEFAULT_BUILD_PRODUCTS)
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-loongson3a-Release-Docker.json
similarity index 61%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-loongson3a-Release-Docker.json
index 10f0ca0..2a3d723 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-loongson3a-Release-Docker.json
@@ -1,67 +1,89 @@
 [
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/cast_toolchain/VERSION",
-      "/path/to/tmp/"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-loongson3a-Release-Docker/Release"
     ],
     "infra_step": true,
-    "name": "Get cast_toolchain VERSION"
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "chmod",
+      "777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-loongson3a-Release-Docker/Release"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "fetch-gn"
+    "name": "Docker setup.chmod 777 [START_DIR]/cache/work/skia/out/Build-Debian10-GCC-loongson3a-Release-Docker/Release",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
-      "--args=ar=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabihf-ar\" cc=\"[START_DIR]/cast_toolchain/armv7a/usr/bin/clang-9.elf\" cxx=\"[START_DIR]/cast_toolchain/armv7a/usr/bin/clang++-9.elf\" extra_asmflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\"] extra_cflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\", \"--sysroot\", \"[START_DIR]/cast_toolchain/armv7a/usr/armv7a-cros-linux-gnueabihf\", \"-I[START_DIR]/chromebook_arm_gles/include\", \"-DMESA_EGL_NO_X11_HEADERS\", \"-DSK_NO_COMMAND_BUFFER\", \"-Wno-error=unused-function\", \"-g0\", \"-DDUMMY_cast_toolchain_version=42\", \"-include\", \"[START_DIR]/cache/work/skia/tools/force_older_glibc_math.h\"] extra_ldflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\", \"--sysroot\", \"[START_DIR]/cast_toolchain/armv7a/usr/armv7a-cros-linux-gnueabihf\", \"-static-libstdc++\", \"-static-libgcc\", \"-L[START_DIR]/cast_toolchain/armv7a/lib\", \"-L[START_DIR]/chromebook_arm_gles/lib\", \"-fuse-ld=gold\", \"-B[START_DIR]/cast_toolchain/armv7a/usr/libexec/gcc\"] is_debug=false skia_enable_gpu=true skia_use_egl=true skia_use_fontconfig=false skia_use_icu=false skia_use_system_freetype2=false target_cpu=\"arm\" werror=true"
+      "chmod",
+      "755",
+      "[START_DIR]/cache/work"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]/cache/work",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
-      "nanobench",
-      "dm"
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::build]/resources/docker-compile.sh"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work,target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-loongson3a-Release-Docker/Release,target=/OUT",
+      "gcr.io/skia-public/gcc-debian10-mips64el@sha256:c173a718d9f62f0cd1e5335713ebc4721d5dcf662fb02597744b71c53338a540",
+      "/SRC/../RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+      "cc=\"/usr/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"/usr/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_docker_image=gcr.io/skia-public/gcc-debian10-mips64el@sha256:c173a718d9f62f0cd1e5335713ebc4721d5dcf662fb02597744b71c53338a540\"] extra_ldflags=[] is_debug=false target_cpu=\"loongson3a\" werror=true"
+    ],
     "env": {
       "CHROME_HEADLESS": "1",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "ninja"
+    "name": "Run build script in Docker"
   },
   {
     "cmd": [
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-loongson3a-Release-Docker/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86-Debug-Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86-Debug-Docker.json
index bfc835b..5788145 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86-Debug-Docker.json
@@ -1,68 +1,90 @@
 [
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
-      "/path/to/tmp/"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86-Debug-Docker/Debug"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "chmod",
+      "777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86-Debug-Docker/Debug"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "fetch-gn"
+    "name": "Docker setup.chmod 777 [START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86-Debug-Docker/Debug",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "chmod",
+      "755",
+      "[START_DIR]/cache/work"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]/cache/work",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::build]/resources/docker-compile.sh"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work,target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86-Debug-Docker/Debug,target=/OUT",
+      "gcr.io/skia-public/gcc-debian10-x86@sha256:b1ec55403ac66d9500d033d6ffd7663894d32335711fbbb0fb4c67dfce812203",
+      "/SRC/../RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+      "cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-DDUMMY_docker_image=gcr.io/skia-public/gcc-debian10-x86@sha256:b1ec55403ac66d9500d033d6ffd7663894d32335711fbbb0fb4c67dfce812203\"] extra_ldflags=[] target_cpu=\"x86\" werror=true"
+    ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "ninja"
+    "name": "Run build script in Docker"
   },
   {
     "cmd": [
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86-Debug-Docker/Debug",
+      "[START_DIR]/[SWARM_OUT_DIR]/out/Debug"
     ],
     "infra_step": true,
     "name": "copy build products",
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json
index bfc835b..3a0a196 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json
@@ -1,68 +1,90 @@
 [
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
-      "/path/to/tmp/"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Debug-Docker/Debug"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "chmod",
+      "777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Debug-Docker/Debug"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "fetch-gn"
+    "name": "Docker setup.chmod 777 [START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Debug-Docker/Debug",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "chmod",
+      "755",
+      "[START_DIR]/cache/work"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]/cache/work",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::build]/resources/docker-compile.sh"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work,target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Debug-Docker/Debug,target=/OUT",
+      "gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c",
+      "/SRC/../RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+      "cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-DDUMMY_docker_image=gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c\"] extra_ldflags=[] target_cpu=\"x86_64\" werror=true"
+    ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "ninja"
+    "name": "Run build script in Docker"
   },
   {
     "cmd": [
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Debug-Docker/Debug",
+      "[START_DIR]/[SWARM_OUT_DIR]/out/Debug"
     ],
     "infra_step": true,
     "name": "copy build products",
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json
index bfc835b..ef87b5c 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json
@@ -1,67 +1,89 @@
 [
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
-      "/path/to/tmp/"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker/Release"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "chmod",
+      "777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker/Release"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "fetch-gn"
+    "name": "Docker setup.chmod 777 [START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker/Release",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "chmod",
+      "755",
+      "[START_DIR]/cache/work"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]/cache/work",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::build]/resources/docker-compile.sh"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work,target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker/Release,target=/OUT",
+      "gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c",
+      "/SRC/../RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+      "cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-DDUMMY_docker_image=gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c\"] extra_ldflags=[] is_debug=false skia_enable_gpu=false target_cpu=\"x86_64\" werror=true"
+    ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "ninja"
+    "name": "Run build script in Docker"
   },
   {
     "cmd": [
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json
index bfc835b..d9177ed 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json
@@ -1,67 +1,89 @@
 [
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
-      "/path/to/tmp/"
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-Shared_Docker/Release"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "chmod",
+      "777",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-Shared_Docker/Release"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "fetch-gn"
+    "name": "Docker setup.chmod 777 [START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-Shared_Docker/Release",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "chmod",
+      "755",
+      "[START_DIR]/cache/work"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]/cache/work",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
   },
   {
     "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::build]/resources/docker-compile.sh"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work,target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-Shared_Docker/Release,target=/OUT",
+      "gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c",
+      "/SRC/../RECIPE_MODULE[skia::build]/resources/docker-compile.sh",
+      "cc=\"gcc\" cxx=\"g++\" extra_cflags=[\"-DDUMMY_docker_image=gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c\"] extra_ldflags=[] is_component_build=true is_debug=false target_cpu=\"x86_64\" werror=true"
+    ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "ninja"
+    "name": "Run build script in Docker"
   },
   {
     "cmd": [
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian10-GCC-x86_64-Release-Shared_Docker/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json
index fde9337..1596a6b 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get android_ndk_linux VERSION"
+    "name": "Get android_ndk_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json
index 850576b..cdffdb6 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get android_ndk_linux VERSION"
+    "name": "Get android_ndk_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json
index e107452..616961f 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromecast.json
similarity index 92%
rename from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json
rename to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromecast.json
index 10f0ca0..22a3fb2 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-arm-Release-Chromecast.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromecast.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get cast_toolchain VERSION"
+    "name": "Get cast_toolchain VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -31,7 +35,7 @@
     "cmd": [
       "[START_DIR]/cache/work/skia/bin/gn",
       "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm-Release-Chromecast/Release",
       "--args=ar=\"[START_DIR]/cast_toolchain/armv7a/bin/armv7a-cros-linux-gnueabihf-ar\" cc=\"[START_DIR]/cast_toolchain/armv7a/usr/bin/clang-9.elf\" cxx=\"[START_DIR]/cast_toolchain/armv7a/usr/bin/clang++-9.elf\" extra_asmflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\"] extra_cflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\", \"--sysroot\", \"[START_DIR]/cast_toolchain/armv7a/usr/armv7a-cros-linux-gnueabihf\", \"-I[START_DIR]/chromebook_arm_gles/include\", \"-DMESA_EGL_NO_X11_HEADERS\", \"-DSK_NO_COMMAND_BUFFER\", \"-Wno-error=unused-function\", \"-g0\", \"-DDUMMY_cast_toolchain_version=42\", \"-include\", \"[START_DIR]/cache/work/skia/tools/force_older_glibc_math.h\"] extra_ldflags=[\"-target\", \"armv7a-cros-linux-gnueabihf\", \"--sysroot\", \"[START_DIR]/cast_toolchain/armv7a/usr/armv7a-cros-linux-gnueabihf\", \"-static-libstdc++\", \"-static-libgcc\", \"-L[START_DIR]/cast_toolchain/armv7a/lib\", \"-L[START_DIR]/chromebook_arm_gles/lib\", \"-fuse-ld=gold\", \"-B[START_DIR]/cast_toolchain/armv7a/usr/libexec/gcc\"] is_debug=false skia_enable_gpu=true skia_use_egl=true skia_use_fontconfig=false skia_use_icu=false skia_use_system_freetype2=false target_cpu=\"arm\" werror=true"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
@@ -45,7 +49,7 @@
     "cmd": [
       "ninja",
       "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm-Release-Chromecast/Release",
       "nanobench",
       "dm"
     ],
@@ -61,7 +65,7 @@
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-arm-Release-Chromecast/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm-Release-Chromecast/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json
similarity index 63%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json
index 29fb6d2..3a506e3 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json
@@ -1,6 +1,24 @@
 [
   {
     "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/android_ndk_linux/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get android_ndk_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]/cache/work/skia/bin/fetch-gn"
@@ -17,8 +35,8 @@
     "cmd": [
       "[START_DIR]/cache/work/skia/bin/gn",
       "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_enable_gpu=false target_cpu=\"x86_64\" werror=true"
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm64-Release-Android_Wuffs/Release",
+      "--args=extra_cflags=[\"-DDUMMY_ndk_version=42\"] is_debug=false ndk=\"[START_DIR]/android_ndk_linux\" skia_use_wuffs=true target_cpu=\"arm64\" werror=true"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
@@ -31,7 +49,7 @@
     "cmd": [
       "ninja",
       "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release"
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm64-Release-Android_Wuffs/Release"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
@@ -44,8 +62,8 @@
     "cmd": [
       "python",
       "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release",
+      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'nanobench', 'skpbench']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-arm64-Release-Android_Wuffs/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
@@ -59,7 +77,7 @@
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
       "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
+      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'nanobench', 'skpbench']@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@try:@@@",
       "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json
index ac5834f..81b5a2b 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json
index c9acc8a..e5532c1 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-MSAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-MSAN.json
index 3e6a3ef..04564da 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-MSAN.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-MSAN.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json
index ed92d9a..a7adfbc 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json
index aa59d14..8437ee7 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json
index d31d03c..78c25cc 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json
index be1c414..7f0dd25 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json
index ca6d65d..5e69159 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json
index 4faca27..991e648 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ANGLE.json
similarity index 79%
rename from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
rename to infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ANGLE.json
index bfc835b..0389e08 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ANGLE.json
@@ -7,11 +7,15 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -31,13 +35,12 @@
     "cmd": [
       "[START_DIR]/cache/work/skia/bin/gn",
       "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ANGLE/Release",
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false skia_use_angle=true target_cpu=\"x86_64\" werror=true"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
     "name": "gn gen"
@@ -46,12 +49,11 @@
     "cmd": [
       "ninja",
       "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ANGLE/Release"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
     "name": "ninja"
@@ -61,7 +63,7 @@
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
+      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ANGLE/Release",
       "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
     ],
     "infra_step": true,
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json
index 6d546ef..3660a07 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json
index b910eaf..d95b384 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
index 18d2be7..f98dd33 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json
index 0583da8..fb0769f 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json
index 2efd371..bd4cd0c 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Vulkan.json
index b893230..68a951e 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Vulkan.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Vulkan.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_linux VERSION"
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-ANGLE.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-ANGLE.json
deleted file mode 100644
index b54d262..0000000
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-ANGLE.json
+++ /dev/null
@@ -1,84 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-ANGLE/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_use_angle=true target_cpu=\"x86_64\" werror=true"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-ANGLE/Release"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "ninja"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-ANGLE/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
-    ],
-    "infra_step": true,
-    "name": "copy build products",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-Shared.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-Shared.json
deleted file mode 100644
index 12b83d8..0000000
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-Shared.json
+++ /dev/null
@@ -1,84 +0,0 @@
-[
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-Shared/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_component_build=true is_debug=false target_cpu=\"x86_64\" werror=true"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-Shared/Release"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "ninja"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-Shared/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
-    ],
-    "infra_step": true,
-    "name": "copy build products",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json
index f09ae33..ab3e9b5 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get android_ndk_darwin VERSION"
+    "name": "Get android_ndk_darwin VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json
index 2b4878f..295a25c 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get android_ndk_windows VERSION"
+    "name": "Get android_ndk_windows VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json
index c8eaaf9..7f16b78 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_win VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json
similarity index 70%
rename from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json
rename to infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json
index 29fb6d2..8c9d108 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-x86_64-Release-NoGPU.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json
@@ -1,29 +1,47 @@
 [
   {
     "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]\\cache\\work\\skia\\infra\\bots\\assets\\clang_win\\VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "[START_DIR]\\cache\\work\\skia\\bin\\fetch-gn"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "infra_step": true,
     "name": "fetch-gn"
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
+      "[START_DIR]\\cache\\work\\skia\\bin\\gn",
       "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release",
-      "--args=cc=\"gcc\" cxx=\"g++\" is_debug=false skia_enable_gpu=false target_cpu=\"x86_64\" werror=true"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Debug-ANGLE\\Debug_x64",
+      "--args=cc=\"clang\" clang_win=\"[START_DIR]\\clang_win\" cxx=\"clang++\" extra_cflags=[\"-O1\", \"-DDUMMY_clang_win_version=42\"] skia_use_angle=true target_cpu=\"x86_64\" werror=true win_sdk=\"[START_DIR]\\win_toolchain/win_sdk\" win_vc=\"[START_DIR]\\win_toolchain/VC\""
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "name": "gn gen"
   },
@@ -31,12 +49,12 @@
     "cmd": [
       "ninja",
       "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Debug-ANGLE\\Debug_x64"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "name": "ninja"
   },
@@ -45,8 +63,8 @@
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-x86_64-Release-NoGPU/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Debug-ANGLE\\Debug_x64",
+      "[START_DIR]\\[SWARM_OUT_DIR]\\out\\Debug_x64"
     ],
     "infra_step": true,
     "name": "copy build products",
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json
index e6eba06..a7c3fec 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-OpenCL.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_win VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json
similarity index 70%
copy from infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
copy to infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json
index bfc835b..9243394 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-GCC-loongson3a-Release.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json
@@ -3,42 +3,45 @@
     "cmd": [
       "vpython",
       "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/mips64el_toolchain_linux/VERSION",
+      "[START_DIR]\\cache\\work\\skia\\infra\\bots\\assets\\clang_win\\VERSION",
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get mips64el_toolchain_linux VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
       "python",
       "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
+      "[START_DIR]\\cache\\work\\skia\\bin\\fetch-gn"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "infra_step": true,
     "name": "fetch-gn"
   },
   {
     "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
+      "[START_DIR]\\cache\\work\\skia\\bin\\gn",
       "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "--args=cc=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-gcc-8\" cxx=\"[START_DIR]/mips64el_toolchain_linux/bin/mips64el-linux-gnuabi64-g++-8\" extra_cflags=[\"-DDUMMY_mips64el_toolchain_linux_version=42\"] extra_ldflags=[\"-L[START_DIR]/mips64el_toolchain_linux/mips64el-linux-gnuabi64/lib\"] is_debug=false skia_enable_gpu=false skia_use_fontconfig=false skia_use_system_freetype2=false target_cpu=\"loongson3a\" werror=false"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Release-Shared\\Release_x64",
+      "--args=cc=\"clang\" clang_win=\"[START_DIR]\\clang_win\" cxx=\"clang++\" extra_cflags=[\"-DDUMMY_clang_win_version=42\"] is_component_build=true is_debug=false target_cpu=\"x86_64\" werror=true win_sdk=\"[START_DIR]\\win_toolchain/win_sdk\" win_vc=\"[START_DIR]\\win_toolchain/VC\""
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "name": "gn gen"
   },
@@ -46,13 +49,12 @@
     "cmd": [
       "ninja",
       "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Release-Shared\\Release_x64"
     ],
-    "cwd": "[START_DIR]/cache/work/skia",
+    "cwd": "[START_DIR]\\cache\\work\\skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/mips64el_toolchain_linux/lib/x86_64-linux-gnu/",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
     },
     "name": "ninja"
   },
@@ -61,8 +63,8 @@
       "python",
       "-u",
       "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-GCC-loongson3a-Release/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/out/Release"
+      "[START_DIR]\\cache\\work\\skia\\out\\Build-Win-Clang-x86_64-Release-Shared\\Release_x64",
+      "[START_DIR]\\[SWARM_OUT_DIR]\\out\\Release_x64"
     ],
     "infra_step": true,
     "name": "copy build products",
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json
index a200fcf..6024779 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_win VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json b/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
index 6cde17c..d7f3798 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
@@ -1,6 +1,24 @@
 [
   {
     "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]/cache/work/skia/bin/fetch-gn"
@@ -32,7 +50,7 @@
       "[START_DIR]/cache/work/skia/bin/gn",
       "gen",
       "[START_DIR]/cache/work/skia/out/Housekeeper-PerCommit-CheckGeneratedFiles/Release",
-      "--args=is_debug=false skia_compile_processors=true skia_generate_workarounds=true werror=true"
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false skia_compile_processors=true skia_generate_workarounds=true werror=true"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json b/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json
index 869ce08..5027d9d 100644
--- a/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json
+++ b/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get android_ndk_linux VERSION"
+    "name": "Get android_ndk_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json b/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json
new file mode 100644
index 0000000..413799b
--- /dev/null
+++ b/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json
@@ -0,0 +1,35 @@
+[
+  {
+    "cmd": [],
+    "name": "RECIPE CRASH (Uncaught exception)",
+    "~followup_annotations": [
+      "@@@STEP_EXCEPTION@@@",
+      "The recipe has crashed at point 'Uncaught exception'!",
+      "",
+      "Traceback (most recent call last):",
+      "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/engine.py\", in run_steps",
+      "    raw_result = recipe_obj.run_steps(api, engine)",
+      "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/recipe_deps.py\", in run_steps",
+      "    properties_def, api=api)",
+      "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in invoke_with_properties",
+      "    arg_names, **additional_args)",
+      "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
+      "    return callable_obj(*props, **additional_args)",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipe_modules/build/examples/full.py\", line 22, in RunSteps",
+      "    api.build(checkout_root=checkout_root, out_dir=out_dir)",
+      "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/recipe_api.py\", in _inner",
+      "    return func(*a, **kw)",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipe_modules/build/api.py\", line 61, in __call__",
+      "    self.compile_fn(self.m, checkout_root, out_dir)",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipe_modules/build/docker.py\", line 78, in compile_fn",
+      "    raise Exception('Not implemented: ' + api.vars.builder_name)",
+      "Exception: Not implemented: Build-Unix-GCC-x86_64-Release-Docker"
+    ]
+  },
+  {
+    "failure": {
+      "humanReason": "Uncaught Exception: Exception('Not implemented: Build-Unix-GCC-x86_64-Release-Docker',)"
+    },
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/build/examples/full.py b/infra/bots/recipe_modules/build/examples/full.py
index 32f922f..7a3f428 100644
--- a/infra/bots/recipe_modules/build/examples/full.py
+++ b/infra/bots/recipe_modules/build/examples/full.py
@@ -26,10 +26,17 @@
 
 
 TEST_BUILDERS = [
+  'Build-Debian10-GCC-loongson3a-Release-Docker',
+  'Build-Debian10-GCC-x86-Debug-Docker',
+  'Build-Debian10-GCC-x86_64-Debug-Docker',
+  'Build-Debian10-GCC-x86_64-Release-NoGPU_Docker',
+  'Build-Debian10-GCC-x86_64-Release-Shared_Docker',
   'Build-Debian9-Clang-arm-Release-Android_API26',
   'Build-Debian9-Clang-arm-Release-Android_ASAN',
   'Build-Debian9-Clang-arm-Release-Chromebook_GLES',
+  'Build-Debian9-Clang-arm-Release-Chromecast',
   'Build-Debian9-Clang-arm-Release-Flutter_Android',
+  'Build-Debian9-Clang-arm64-Release-Android_Wuffs',
   'Build-Debian9-Clang-x86-devrel-Android_SKQP',
   'Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES',
   'Build-Debian9-Clang-x86_64-Debug-Coverage',
@@ -40,6 +47,7 @@
   'Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN',
   'Build-Debian9-Clang-x86_64-Debug-Tidy',
   'Build-Debian9-Clang-x86_64-Debug-Wuffs',
+  'Build-Debian9-Clang-x86_64-Release-ANGLE',
   'Build-Debian9-Clang-x86_64-Release-ASAN',
   'Build-Debian9-Clang-x86_64-Release-CMake',
   'Build-Debian9-Clang-x86_64-Release-Fast',
@@ -53,11 +61,6 @@
   'Build-Debian9-EMCC-wasm-Debug-PathKit',
   'Build-Debian9-EMCC-wasm-Release-CanvasKit_CPU',
   'Build-Debian9-EMCC-wasm-Release-PathKit',
-  'Build-Debian9-GCC-arm-Release-Chromecast',
-  'Build-Debian9-GCC-loongson3a-Release',
-  'Build-Debian9-GCC-x86_64-Release-ANGLE',
-  'Build-Debian9-GCC-x86_64-Release-NoGPU',
-  'Build-Debian9-GCC-x86_64-Release-Shared',
   'Build-Mac-Clang-arm-Debug-iOS',
   'Build-Mac-Clang-arm64-Debug-Android_Vulkan',
   'Build-Mac-Clang-arm64-Debug-iOS',
@@ -67,7 +70,9 @@
   'Build-Mac-Clang-x86_64-Release-MoltenVK_Vulkan',
   'Build-Win-Clang-arm64-Release-Android',
   'Build-Win-Clang-x86-Debug-Exceptions',
+  'Build-Win-Clang-x86_64-Debug-ANGLE',
   'Build-Win-Clang-x86_64-Debug-OpenCL',
+  'Build-Win-Clang-x86_64-Release-Shared',
   'Build-Win-Clang-x86_64-Release-Vulkan',
   'Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP',
   'Housekeeper-PerCommit-CheckGeneratedFiles',
@@ -92,3 +97,9 @@
     if 'Win' in buildername and not 'LenovoYogaC630' in buildername:
       test += api.platform('win', 64)
     yield test
+
+  yield (
+      api.test('unknown-docker-image') +
+      api.properties(**defaultProps('Build-Unix-GCC-x86_64-Release-Docker')) +
+      api.expect_exception('Exception')
+  )
diff --git a/infra/bots/recipe_modules/build/resources/docker-compile.sh b/infra/bots/recipe_modules/build/resources/docker-compile.sh
new file mode 100755
index 0000000..57c9a66
--- /dev/null
+++ b/infra/bots/recipe_modules/build/resources/docker-compile.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Copyright 2019 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This assumes it is being run inside a docker container with the following
+# mounts:
+#  /SRC: Swarming start dir
+#  /OUT: output directory for gn and ninja
+
+set -e
+set -x
+
+export PATH="/SRC/recipe_bundle/depot_tools:${PATH}"
+
+cd /SRC/skia
+./bin/fetch-gn
+./bin/gn gen /OUT "--args=$1"
+ninja -C /OUT
diff --git a/infra/bots/recipe_modules/builder_name_schema/api.py b/infra/bots/recipe_modules/builder_name_schema/api.py
index 8686111..b0d67b1 100644
--- a/infra/bots/recipe_modules/builder_name_schema/api.py
+++ b/infra/bots/recipe_modules/builder_name_schema/api.py
@@ -24,7 +24,6 @@
     self.BUILDER_ROLE_INFRA = builder_name_schema.BUILDER_ROLE_INFRA
     self.BUILDER_ROLE_PERF = builder_name_schema.BUILDER_ROLE_PERF
     self.BUILDER_ROLE_TEST = builder_name_schema.BUILDER_ROLE_TEST
-    self.BUILDER_ROLE_CALMBENCH = builder_name_schema.BUILDER_ROLE_CALMBENCH
     self.BUILDER_ROLES = builder_name_schema.BUILDER_ROLES
 
   def MakeBuilderName(self, **kwargs):
diff --git a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
index 18777e4..6958b16 100644
--- a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
+++ b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json
@@ -22,21 +22,6 @@
         "extra_config"
       ]
     },
-    "Calmbench": {
-      "keys": [
-        "os",
-        "compiler",
-        "model",
-        "cpu_or_gpu",
-        "cpu_or_gpu_value",
-        "arch",
-        "configuration",
-        "test_filter"
-      ],
-      "optional_keys": [
-        "extra_config"
-      ]
-    },
     "Housekeeper": {
       "keys": [
         "frequency"
@@ -87,7 +72,6 @@
       "recurse_roles": [
         "Build",
         "BuildStats",
-        "Calmbench",
         "Perf",
         "Test"
       ]
diff --git a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
index a596be8..fff45e9 100644
--- a/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
+++ b/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.py
@@ -22,7 +22,6 @@
 # Builder roles.
 BUILDER_ROLE_BUILD = 'Build'
 BUILDER_ROLE_BUILDSTATS = 'BuildStats'
-BUILDER_ROLE_CALMBENCH = 'Calmbench'
 BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper'
 BUILDER_ROLE_INFRA = 'Infra'
 BUILDER_ROLE_PERF = 'Perf'
@@ -30,7 +29,6 @@
 BUILDER_ROLE_UPLOAD = 'Upload'
 BUILDER_ROLES = (BUILDER_ROLE_BUILD,
                  BUILDER_ROLE_BUILDSTATS,
-                 BUILDER_ROLE_CALMBENCH,
                  BUILDER_ROLE_HOUSEKEEPER,
                  BUILDER_ROLE_INFRA,
                  BUILDER_ROLE_PERF,
diff --git a/infra/bots/recipe_modules/checkout/api.py b/infra/bots/recipe_modules/checkout/api.py
index 0d2f3bd..48ea072 100644
--- a/infra/bots/recipe_modules/checkout/api.py
+++ b/infra/bots/recipe_modules/checkout/api.py
@@ -17,8 +17,23 @@
     """The default location for cached persistent checkouts."""
     return self.m.vars.cache_dir.join('work')
 
+  def assert_git_is_from_cipd(self):
+    """Fail if git is not obtained from CIPD."""
+    self.m.run(self.m.python.inline, 'Assert that Git is from CIPD', program='''
+import subprocess
+import sys
+
+which = 'where' if sys.platform == 'win32' else 'which'
+git = subprocess.check_output([which, 'git'])
+print 'git was found at %s' % git
+if 'cipd_bin_packages' not in git:
+  print >> sys.stderr, 'Git must be obtained through CIPD.'
+  sys.exit(1)
+''')
+
   def git(self, checkout_root):
     """Run the steps to perform a pure-git checkout without DEPS."""
+    self.assert_git_is_from_cipd()
     skia_dir = checkout_root.join('skia')
     self.m.git.checkout(
         self.m.properties['repository'], dir_path=skia_dir,
@@ -31,7 +46,7 @@
 
   def bot_update(self, checkout_root, gclient_cache=None,
                  checkout_chromium=False, checkout_flutter=False,
-                 extra_gclient_env=None, parent_rev=False,
+                 extra_gclient_env=None,
                  flutter_android=False):
     """Run the steps to obtain a checkout using bot_update.
 
@@ -44,11 +59,9 @@
           primary repo.
       extra_gclient_env: Map of extra environment variable names to their values
           to supply while running gclient.
-      parent_rev: If True, checks out the parent of the specified revision,
-          rather than the revision itself, ie. HEAD^ for normal jobs and HEAD
-          (no patch) for try jobs.
       flutter_android: Indicates that we're checking out flutter for Android.
     """
+    self.assert_git_is_from_cipd()
     if not gclient_cache:
       gclient_cache = self.m.vars.cache_dir.join('git')
     if not extra_gclient_env:
@@ -119,9 +132,6 @@
                          entries_file)
 
     # Run bot_update.
-    if not self.m.vars.is_trybot and parent_rev:
-      main.revision = main.revision + '^'
-
     patch_refs = None
     patch_ref = self.m.properties.get('patch_ref')
     if patch_ref:
@@ -136,8 +146,8 @@
           # The logic in ensure_checkout for this arg is fairly naive, so if
           # patch=False, we'll see "... (without patch)" in the step names, even
           # for non-trybot runs, which is misleading and confusing. Therefore,
-          # always specify patch=True for non-trybot runs.
-          patch=not (self.m.vars.is_trybot and parent_rev),
+          # always specify patch=True.
+          patch=True,
           patch_refs=patch_refs,
       )
 
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json b/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
index 67c4c85..795bb61 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json
@@ -3,6 +3,31 @@
     "cmd": [
       "python",
       "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
       "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
       "--path",
       "[START_DIR]/skia",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
index 2ee4c7d..ecc1835 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Win-Clang-x86_64-Release-ParentRevision.json b/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Win-Clang-x86_64-Release-ParentRevision.json
deleted file mode 100644
index 8879ec4..0000000
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Win-Clang-x86_64-Release-ParentRevision.json
+++ /dev/null
@@ -1,114 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]\\cache\\work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]\\cache\\work\\.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]\\cache\\work\\.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]\\\\cache\\\\git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]\\cache\\git",
-      "--cleanup-dir",
-      "[CLEANUP]\\bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123^"
-    ],
-    "cwd": "[START_DIR]\\cache\\work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123^\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]\\tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
index 8714270..efe1171 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json b/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json
index c325335..579b384 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json b/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json
index 27ba386..1435099 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/flutter_trybot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/parent_revision_trybot.json b/infra/bots/recipe_modules/checkout/examples/full.expected/parent_revision_trybot.json
deleted file mode 100644
index 8d8ae6e..0000000
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/parent_revision_trybot.json
+++ /dev/null
@@ -1,114 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]\\cache\\work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]\\cache\\work\\.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]\\cache\\work\\.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]\\resources\\bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]\\\\cache\\\\git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]\\cache\\git",
-      "--cleanup-dir",
-      "[CLEANUP]\\bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]\\cache\\work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update (without patch)",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]\\tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json b/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json
index e577cb6..d1587c4 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json
+++ b/infra/bots/recipe_modules/checkout/examples/full.expected/trybot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipe_modules/checkout/examples/full.py b/infra/bots/recipe_modules/checkout/examples/full.py
index 3d8a1e0..5cb0aaf 100644
--- a/infra/bots/recipe_modules/checkout/examples/full.py
+++ b/infra/bots/recipe_modules/checkout/examples/full.py
@@ -26,7 +26,6 @@
   checkout_flutter = False
   extra_gclient_env = {}
   flutter_android = False
-  parent_rev = False
   if 'CommandBuffer' in api.vars.builder_name:
     checkout_chromium = True
   if 'RecreateSKPs' in api.vars.builder_name:
@@ -38,8 +37,6 @@
     checkout_flutter = True
     if 'Android' in api.vars.builder_name:
       flutter_android = True
-  if 'ParentRevision' in api.vars.builder_name:
-    parent_rev = True
 
   if bot_update:
     api.checkout.bot_update(
@@ -47,15 +44,13 @@
         checkout_chromium=checkout_chromium,
         checkout_flutter=checkout_flutter,
         extra_gclient_env=extra_gclient_env,
-        flutter_android=flutter_android,
-        parent_rev=parent_rev)
+        flutter_android=flutter_android)
   else:
     api.checkout.git(checkout_root=api.path['start_dir'])
   api.file.ensure_directory('makedirs tmp_dir', api.vars.tmp_dir)
 
 
 TEST_BUILDERS = [
-  'Build-Win-Clang-x86_64-Release-ParentRevision',
   'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
   'Housekeeper-Weekly-RecreateSKPs',
 ]
@@ -71,26 +66,8 @@
                        path_config='kitchen',
                        swarm_out_dir='[SWARM_OUT_DIR]')
     )
-    if 'Win' in buildername and not 'LenovoYogaC630' in buildername:
-      test += api.platform('win', 64)
     yield test
 
-  buildername = 'Build-Win-Clang-x86_64-Release-ParentRevision'
-  yield (
-      api.test('parent_revision_trybot') +
-      api.properties(buildername=buildername,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]',
-                     patch_issue=456789,
-                     patch_set=12,
-                     patch_ref='refs/changes/89/456789/12',
-                     patch_repo='https://skia.googlesource.com/skia.git',
-                     patch_storage='gerrit') +
-      api.platform('win', 64)
-  )
-
   buildername = 'Build-Debian9-Clang-arm-Release-Flutter_Android'
   yield (
       api.test('flutter_trybot') +
@@ -126,7 +103,7 @@
       api.path.exists(api.path['start_dir'].join('skp_output'))
   )
 
-  buildername = 'Build-Debian9-GCC-x86_64-Release'
+  buildername = 'Build-Debian9-Clang-x86_64-Release'
   yield (
       api.test('cross_repo_trybot') +
       api.properties(
diff --git a/infra/bots/recipe_modules/docker/__init__.py b/infra/bots/recipe_modules/docker/__init__.py
new file mode 100644
index 0000000..5c74755
--- /dev/null
+++ b/infra/bots/recipe_modules/docker/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2019 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+DEPS = [
+  'env',
+  'recipe_engine/file',
+  'recipe_engine/path',
+  'recipe_engine/step',
+  'run',
+]
diff --git a/infra/bots/recipe_modules/docker/api.py b/infra/bots/recipe_modules/docker/api.py
new file mode 100644
index 0000000..8426676
--- /dev/null
+++ b/infra/bots/recipe_modules/docker/api.py
@@ -0,0 +1,78 @@
+# Copyright 2019 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+import os
+from recipe_engine import recipe_api
+
+
+MOUNT_SRC = '/SRC'
+MOUNT_OUT = '/OUT'
+
+
+class DockerApi(recipe_api.RecipeApi):
+  def _chmod(self, filepath, mode, recursive=False):
+    cmd = ['chmod']
+    if recursive:
+      cmd.append('-R')
+    cmd.extend([mode, filepath])
+    name = ' '.join([str(elem) for elem in cmd])
+    self.m.step(name, cmd=cmd, infra_step=True)
+
+  def mount_src(self):
+    return MOUNT_SRC
+
+  def mount_out(self):
+    return MOUNT_OUT
+
+  def run(self, name, docker_image, src_dir, out_dir, script, args=None, docker_args=None, copies=None, recursive_read=None, attempts=1):
+    # Setup. Docker runs as a different user, so we need to give it access to
+    # read, write, and execute certain files.
+    with self.m.step.nest('Docker setup'):
+      # Make sure out_dir exists, otherwise mounting will fail.
+      # (Note that the docker --mount option, unlike the --volume option, does
+      # not create this dir as root if it doesn't exist.)
+      self.m.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
+      # ensure_directory won't change the permissions if the dir already exists,
+      # so we need to do that explicitly.
+      self._chmod(out_dir, '777')
+
+      # chmod the src_dir, but not recursively; Swarming writes some files which
+      # we can't access, so "chmod -R" will fail if this is the root workdir.
+      self._chmod(src_dir, '755')
+
+      # Need to make the script executable, or Docker can't run it.
+      self._chmod(script, '0755')
+
+      # Copy any requested files.
+      if copies:
+        for src, dest in copies.iteritems():
+          dirname = self.m.path.dirname(dest)
+          self.m.file.ensure_directory(
+              'mkdirs %s' % dirname, dirname, mode=0777)
+          self.m.file.copy('cp %s %s' % (src, dest), src, dest)
+          self._chmod(dest, '644')
+
+      # Recursive chmod any requested directories.
+      if recursive_read:
+        for elem in recursive_read:
+          self._chmod(elem, 'a+r', recursive=True)
+
+    # Run.
+    cmd = [
+      'docker', 'run', '--shm-size=2gb', '--rm',
+      '--mount', 'type=bind,source=%s,target=%s' % (src_dir, MOUNT_SRC),
+      '--mount', 'type=bind,source=%s,target=%s' % (out_dir, MOUNT_OUT),
+    ]
+    if docker_args:
+      cmd.extend(docker_args)
+    script_rel = os.path.relpath(str(script), str(self.m.path['start_dir']))
+    cmd.extend([docker_image, MOUNT_SRC + '/' + script_rel])
+    if args:
+      cmd.extend(args)
+
+    env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
+    with self.m.env(env):
+      self.m.run.with_retry(self.m.step, name, attempts, cmd=cmd)
diff --git a/infra/bots/recipe_modules/docker/examples/full.expected/test.json b/infra/bots/recipe_modules/docker/examples/full.expected/test.json
new file mode 100644
index 0000000..d26a16e
--- /dev/null
+++ b/infra/bots/recipe_modules/docker/examples/full.expected/test.json
@@ -0,0 +1,149 @@
+[
+  {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "/host-out"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "/host-out"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 /host-out",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "/host-src"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 /host-src",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "./do-stuff.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 ./do-stuff.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "/copy-dst"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs /copy-dst",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "/copy-src/myfile",
+      "/copy-dst/myfile"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp /copy-src/myfile /copy-dst/myfile",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "/copy-dst/myfile"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 /copy-dst/myfile",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "/host-src"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r /host-src",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=/host-src,target=/SRC",
+      "--mount",
+      "type=bind,source=/host-out,target=/OUT",
+      "--cpus",
+      "2",
+      "my.docker.image",
+      "/SRC/../do-stuff.sh",
+      "--src",
+      "/SRC",
+      "--out",
+      "/OUT"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "do Docker stuff"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/infra/bots/recipe_modules/docker/examples/full.py b/infra/bots/recipe_modules/docker/examples/full.py
new file mode 100644
index 0000000..390ea94
--- /dev/null
+++ b/infra/bots/recipe_modules/docker/examples/full.py
@@ -0,0 +1,38 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+DEPS = [
+  'docker',
+  'recipe_engine/context',
+  'recipe_engine/properties',
+  'recipe_engine/step',
+  'vars',
+]
+
+
+def RunSteps(api):
+  api.vars.setup()
+  api.docker.run(
+      name='do Docker stuff',
+      docker_image='my.docker.image',
+      src_dir='/host-src',
+      out_dir='/host-out',
+      script='./do-stuff.sh',
+      args=['--src', api.docker.mount_src(), '--out', api.docker.mount_out()],
+      docker_args=['--cpus', '2'],
+      copies={'/copy-src/myfile': '/copy-dst/myfile'},
+      recursive_read=['/host-src'],
+  )
+
+def GenTests(api):
+  yield (api.test('test') +
+         api.properties(buildername='Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit',
+                        buildbucket_build_id='123454321',
+                        revision='abc123',
+                        path_config='kitchen',
+                        gold_hashes_url='https://example.com/hashes.txt',
+                        swarm_out_dir='[SWARM_OUT_DIR]',
+                        task_id='task_12345')
+  )
diff --git a/infra/bots/recipe_modules/flavor/__init__.py b/infra/bots/recipe_modules/flavor/__init__.py
index 28b3d9b..9e6c0b0 100644
--- a/infra/bots/recipe_modules/flavor/__init__.py
+++ b/infra/bots/recipe_modules/flavor/__init__.py
@@ -8,6 +8,7 @@
   'depot_tools/cipd',
   'depot_tools/gclient',
   'depot_tools/git',
+  'docker',
   'env',
   'infra',
   'recipe_engine/context',
diff --git a/infra/bots/recipe_modules/flavor/android.py b/infra/bots/recipe_modules/flavor/android.py
index e4944a6..2cb7631 100644
--- a/infra/bots/recipe_modules/flavor/android.py
+++ b/infra/bots/recipe_modules/flavor/android.py
@@ -26,16 +26,17 @@
     # Data should go in android_data_dir, which may be preserved across runs.
     android_data_dir = '/sdcard/revenge_of_the_skiabot/'
     self.device_dirs = default.DeviceDirs(
-        bin_dir       = '/data/local/tmp/',
-        dm_dir        = android_data_dir + 'dm_out',
-        perf_data_dir = android_data_dir + 'perf',
-        resource_dir  = android_data_dir + 'resources',
-        images_dir    = android_data_dir + 'images',
-        lotties_dir   = android_data_dir + 'lotties',
-        skp_dir       = android_data_dir + 'skps',
-        svg_dir       = android_data_dir + 'svgs',
-        mskp_dir      = android_data_dir + 'mskp',
-        tmp_dir       = android_data_dir)
+        bin_dir        = '/data/local/tmp/',
+        dm_dir         = android_data_dir + 'dm_out',
+        perf_data_dir  = android_data_dir + 'perf',
+        resource_dir   = android_data_dir + 'resources',
+        images_dir     = android_data_dir + 'images',
+        lotties_dir    = android_data_dir + 'lotties',
+        skp_dir        = android_data_dir + 'skps',
+        svg_dir        = android_data_dir + 'svgs',
+        mskp_dir       = android_data_dir + 'mskp',
+        tmp_dir        = android_data_dir,
+        texttraces_dir = android_data_dir + 'text_blob_traces')
 
     # A list of devices we can't root.  If rooting fails and a device is not
     # on the list, we fail the task to avoid perf inconsistencies.
@@ -130,8 +131,8 @@
       # AndroidOne doesn't support ondemand governor. hotplug is similar.
       if device == 'AndroidOne':
         self._set_governor(i, 'hotplug')
-      elif device == 'Pixel3a':
-        # Pixel3a has userspace powersave performance schedutil.
+      elif device in ['Pixel3a', 'Pixel4']:
+        # Pixel3a/4 have userspace powersave performance schedutil.
         # performance seems like a reasonable choice.
         self._set_governor(i, 'performance')
       else:
diff --git a/infra/bots/recipe_modules/flavor/api.py b/infra/bots/recipe_modules/flavor/api.py
index 9f4df02..dc89baf 100644
--- a/infra/bots/recipe_modules/flavor/api.py
+++ b/infra/bots/recipe_modules/flavor/api.py
@@ -12,6 +12,7 @@
 from . import chromebook
 from . import chromecast
 from . import default
+from . import docker
 from . import ios
 from . import valgrind
 from . import win_ssh
@@ -34,6 +35,7 @@
 VERSION_FILE_SKP = 'SKP_VERSION'
 VERSION_FILE_SVG = 'SVG_VERSION'
 VERSION_FILE_MSKP = 'MSKP_VERSION'
+VERSION_FILE_TEXTTRACES = 'TEXTTRACES_VERSION'
 
 VERSION_NONE = -1
 
@@ -49,6 +51,9 @@
   return ('Chromebook' in vars_api.extra_tokens or
           'ChromeOS' in vars_api.builder_cfg.get('os', ''))
 
+def is_docker(vars_api):
+  return 'Docker' in vars_api.extra_tokens
+
 def is_ios(vars_api):
   return ('iOS' in vars_api.extra_tokens or
           'iOS' == vars_api.builder_cfg.get('os', ''))
@@ -73,6 +78,8 @@
       return chromebook.ChromebookFlavor(self)
     if is_android(vars_api) and not is_test_skqp(vars_api):
       return android.AndroidFlavor(self)
+    elif is_docker(vars_api):
+      return docker.DockerFlavor(self)
     elif is_ios(vars_api):
       return ios.iOSFlavor(self)
     elif is_valgrind(vars_api):
@@ -116,9 +123,11 @@
     return self._f.remove_file_on_device(path)
 
   def install(self, skps=False, images=False, lotties=False, svgs=False,
-              resources=False, mskps=False):
+              resources=False, mskps=False, texttraces=False):
     self._f.install()
 
+    if texttraces:
+      self._copy_texttraces()
     # TODO(borenet): Only copy files which have changed.
     if resources:
       self.copy_directory_contents_to_device(
@@ -229,3 +238,17 @@
         self.host_dirs.mskp_dir,
         self.device_dirs.mskp_dir)
     return version
+
+  def _copy_texttraces(self):
+    """Copy the text traces if needed."""
+    version = self.m.run.asset_version('text_blob_traces', self._skia_dir)
+    self.m.run.writefile(
+        self.m.path.join(self.m.vars.tmp_dir, VERSION_FILE_TEXTTRACES),
+        version)
+    self._copy_dir(
+        version,
+        VERSION_FILE_TEXTTRACES,
+        self.m.vars.tmp_dir,
+        self.host_dirs.texttraces_dir,
+        self.device_dirs.texttraces_dir)
+    return version
diff --git a/infra/bots/recipe_modules/flavor/chromebook.py b/infra/bots/recipe_modules/flavor/chromebook.py
index 116b6fd..b24624c 100644
--- a/infra/bots/recipe_modules/flavor/chromebook.py
+++ b/infra/bots/recipe_modules/flavor/chromebook.py
@@ -18,16 +18,17 @@
     super(ChromebookFlavor, self).__init__(m)
     self.chromeos_homedir = '/home/chronos/user/'
     self.device_dirs = default.DeviceDirs(
-      bin_dir       = self.chromeos_homedir + 'bin',
-      dm_dir        = self.chromeos_homedir + 'dm_out',
-      perf_data_dir = self.chromeos_homedir + 'perf',
-      resource_dir  = self.chromeos_homedir + 'resources',
-      images_dir    = self.chromeos_homedir + 'images',
-      lotties_dir   = self.chromeos_homedir + 'lotties',
-      skp_dir       = self.chromeos_homedir + 'skps',
-      svg_dir       = self.chromeos_homedir + 'svgs',
-      mskp_dir      = self.chromeos_homedir + 'mskp',
-      tmp_dir       = self.chromeos_homedir)
+      bin_dir        = self.chromeos_homedir + 'bin',
+      dm_dir         = self.chromeos_homedir + 'dm_out',
+      perf_data_dir  = self.chromeos_homedir + 'perf',
+      resource_dir   = self.chromeos_homedir + 'resources',
+      images_dir     = self.chromeos_homedir + 'images',
+      lotties_dir    = self.chromeos_homedir + 'lotties',
+      skp_dir        = self.chromeos_homedir + 'skps',
+      svg_dir        = self.chromeos_homedir + 'svgs',
+      mskp_dir       = self.chromeos_homedir + 'mskp',
+      tmp_dir        = self.chromeos_homedir,
+      texttraces_dir = '')
 
   def install(self):
     super(ChromebookFlavor, self).install()
diff --git a/infra/bots/recipe_modules/flavor/chromecast.py b/infra/bots/recipe_modules/flavor/chromecast.py
index e0bbfb1..501a1da 100644
--- a/infra/bots/recipe_modules/flavor/chromecast.py
+++ b/infra/bots/recipe_modules/flavor/chromecast.py
@@ -23,16 +23,17 @@
     # on the tempfs (i.e. RAM) /dev/shm. (which is about 140M)
     data_dir = '/cache/skia/'
     self.device_dirs = default.DeviceDirs(
-        bin_dir       = '/cache/skia/bin',
-        dm_dir        = '/dev/shm/skia/dm_out',
-        perf_data_dir = data_dir + 'perf',
-        resource_dir  = data_dir + 'resources',
-        images_dir    = data_dir + 'images',
-        lotties_dir   = data_dir + 'lotties',
-        skp_dir       = data_dir + 'skps',
-        svg_dir       = data_dir + 'svgs',
-        mskp_dir      = data_dir + 'mskp',
-        tmp_dir       = data_dir)
+        bin_dir        = '/cache/skia/bin',
+        dm_dir         = '/dev/shm/skia/dm_out',
+        perf_data_dir  = data_dir + 'perf',
+        resource_dir   = data_dir + 'resources',
+        images_dir     = data_dir + 'images',
+        lotties_dir    = data_dir + 'lotties',
+        skp_dir        = data_dir + 'skps',
+        svg_dir        = data_dir + 'svgs',
+        mskp_dir       = data_dir + 'mskp',
+        tmp_dir        = data_dir,
+        texttraces_dir = '')
 
   @property
   def user_ip_host(self):
diff --git a/infra/bots/recipe_modules/flavor/default.py b/infra/bots/recipe_modules/flavor/default.py
index 827c3d5..ee29f8f 100644
--- a/infra/bots/recipe_modules/flavor/default.py
+++ b/infra/bots/recipe_modules/flavor/default.py
@@ -9,73 +9,18 @@
 """Default flavor, used for running code on desktop machines."""
 
 
+import collections
+
+
 WIN_TOOLCHAIN_DIR = 't'
 
 
-class DeviceDirs(object):
-  def __init__(self,
-               bin_dir,
-               dm_dir,
-               perf_data_dir,
-               resource_dir,
-               images_dir,
-               lotties_dir,
-               skp_dir,
-               svg_dir,
-               mskp_dir,
-               tmp_dir):
-    self._bin_dir = bin_dir
-    self._dm_dir = dm_dir
-    self._perf_data_dir = perf_data_dir
-    self._resource_dir = resource_dir
-    self._images_dir = images_dir
-    self._lotties_dir = lotties_dir
-    self._skp_dir = skp_dir
-    self._svg_dir = svg_dir
-    self._mskp_dir = mskp_dir
-    self._tmp_dir = tmp_dir
-
-  @property
-  def bin_dir(self):
-    return self._bin_dir
-
-  @property
-  def dm_dir(self):
-    """Where DM writes."""
-    return self._dm_dir
-
-  @property
-  def perf_data_dir(self):
-    return self._perf_data_dir
-
-  @property
-  def resource_dir(self):
-    return self._resource_dir
-
-  @property
-  def images_dir(self):
-    return self._images_dir
-
-  @property
-  def lotties_dir(self):
-    return self._lotties_dir
-
-  @property
-  def skp_dir(self):
-    """Holds SKP files that are consumed by RenderSKPs and BenchPictures."""
-    return self._skp_dir
-
-  @property
-  def svg_dir(self):
-    return self._svg_dir
-
-  @property
-  def mskp_dir(self):
-    return self._mskp_dir
-
-  @property
-  def tmp_dir(self):
-    return self._tmp_dir
+# Notes:
+#   dm_dir: Where DM writes.
+#   skp_dir: Holds SKP files that are consumed by RenderSKPs and BenchPictures.
+DeviceDirs = collections.namedtuple(
+    'DeviceDirs', ['bin_dir', 'dm_dir', 'perf_data_dir', 'resource_dir', 'images_dir',
+                   'lotties_dir', 'skp_dir', 'svg_dir', 'mskp_dir', 'tmp_dir', 'texttraces_dir'])
 
 
 class DefaultFlavor(object):
@@ -99,7 +44,8 @@
         skp_dir=self.m.path['start_dir'].join('skp'),
         svg_dir=self.m.path['start_dir'].join('svg'),
         mskp_dir=self.m.path['start_dir'].join('mskp'),
-        tmp_dir=self.m.vars.tmp_dir)
+        tmp_dir=self.m.vars.tmp_dir,
+        texttraces_dir=self.m.path['start_dir'].join('text_blob_traces'))
     self.host_dirs = self.device_dirs
 
   def device_path_join(self, *args):
diff --git a/infra/bots/recipe_modules/flavor/docker.py b/infra/bots/recipe_modules/flavor/docker.py
new file mode 100644
index 0000000..60cf5f6
--- /dev/null
+++ b/infra/bots/recipe_modules/flavor/docker.py
@@ -0,0 +1,69 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+from . import default
+
+"""Docker flavor, used for running inside a Docker container."""
+
+
+# TODO(dogben): Move this mapping to a machine-editable file.
+# TODO(dogben): Use images without extra packages installed.
+IMAGES = {
+    'gcc-debian10': (
+        'gcr.io/skia-public/gcc-debian10@sha256:'
+        '89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c'),
+    'gcc-debian10-x86': (
+        'gcr.io/skia-public/gcc-debian10-x86@sha256:'
+        'b1ec55403ac66d9500d033d6ffd7663894d32335711fbbb0fb4c67dfce812203'),
+}
+
+
+class DockerFlavor(default.DefaultFlavor):
+  def __init__(self, m):
+    super(DockerFlavor, self).__init__(m)
+
+  def _map_host_path_to_docker(self, path):
+    """Returns the path in the Docker container mapped to the given path.
+
+    Returns None if the path is not mapped into the Docker container.
+    """
+    path = str(path)
+    for (docker_dir, host_dir) in [
+        (self.m.docker.mount_out(), str(self.m.vars.swarming_out_dir)),
+        (self.m.docker.mount_src(), str(self.m.path['start_dir'])),
+    ]:
+      if path.startswith(host_dir):
+        return docker_dir + path[len(host_dir):]
+    return None
+
+  def step(self, name, cmd, **unused_kwargs):
+    extra_tokens = self.m.vars.extra_tokens
+    extra_tokens.remove('Docker')
+    os = self.m.vars.builder_cfg.get('os', '')
+    model = self.m.vars.builder_cfg.get('model', '')
+    cpu_or_gpu = self.m.vars.builder_cfg.get('cpu_or_gpu', '')
+    arch = self.m.vars.builder_cfg.get('arch', '')
+
+    image_name = None
+    if (os == 'Debian10' and model == 'GCE' and cpu_or_gpu == 'CPU' and
+        not extra_tokens):
+      if arch == 'x86_64':
+        image_name = 'gcc-debian10'
+      elif arch == 'x86':
+        image_name = 'gcc-debian10-x86'
+
+    if not image_name:  # pragma: nocover
+      raise Exception('Not implemented: ' + self.m.vars.builder_name)
+
+    image_hash = IMAGES[image_name]
+
+    # TODO(dogben): Currently Linux-specific.
+    app = self._map_host_path_to_docker(self.device_dirs.bin_dir.join(cmd[0]))
+    args = [self.m.docker.mount_src(), 'catchsegv', app] + [
+        self._map_host_path_to_docker(x) or x for x in cmd[1:]]
+    self.m.docker.run('symbolized %s in Docker' % name, image_hash,
+                      self.m.path['start_dir'], self.m.vars.swarming_out_dir,
+                      self.module.resource('symbolize_stack_trace.py'),
+                      args=args)
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
index 372cb4e..66dd774 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
index bd7c834..278fcdf 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/internal_bot_2.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NVIDIA_Shield-CPU-TegraX1-arm64-Release-All-Android.json
similarity index 77%
rename from infra/bots/recipes/test.expected/internal_bot_2.json
rename to infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NVIDIA_Shield-CPU-TegraX1-arm64-Release-All-Android.json
index 28ac27f..c36fc22 100644
--- a/infra/bots/recipes/test.expected/internal_bot_2.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-NVIDIA_Shield-CPU-TegraX1-arm64-Release-All-Android.json
@@ -1,21 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -30,6 +15,117 @@
   {
     "cmd": [
       "/opt/infra-android/tools/adb",
+      "push",
+      "file.txt",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push file.txt file.txt"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "cat",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "read file.txt"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "rm",
+      "-f",
+      "file.txt"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm file.txt"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "rmtree",
+      "results_dir"
+    ],
+    "infra_step": true,
+    "name": "rmtree results_dir"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "results_dir"
+    ],
+    "infra_step": true,
+    "name": "makedirs results_dir"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "rm",
+      "-rf",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm device_results_dir"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "device_results_dir"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "mkdir device_results_dir"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
       "shell",
       "mkdir",
       "-p",
@@ -45,6 +141,208 @@
     "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
   },
   {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/skia/infra/bots/assets/text_blob_traces/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get text_blob_traces VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "42",
+      "[START_DIR]/tmp/TEXTTRACES_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write TEXTTRACES_VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@TEXTTRACES_VERSION@42@@@",
+      "@@@STEP_LOG_END@TEXTTRACES_VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [],
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "listdir",
+      "[START_DIR]/text_blob_traces",
+      "--recursive"
+    ],
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.list [START_DIR]/text_blob_traces",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/.file3@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/.ignore/file4@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/file1@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/subdir/file2@@@",
+      "@@@STEP_LOG_END@listdir@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "push",
+      "[START_DIR]/text_blob_traces/.file3",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/.file3"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/.file3",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "push",
+      "[START_DIR]/text_blob_traces/file1",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/file1"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/file1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "push",
+      "[START_DIR]/text_blob_traces/subdir/file2",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/subdir/file2"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/subdir/file2",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/opt/infra-android/tools/adb",
+      "push",
+      "[START_DIR]/tmp/TEXTTRACES_VERSION",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/TEXTTRACES_VERSION /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
     "cmd": [],
     "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources"
   },
@@ -139,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +841,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -723,161 +1033,9 @@
   },
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "rmtree",
-      "[START_DIR]/test"
-    ],
-    "infra_step": true,
-    "name": "rmtree test"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/test"
-    ],
-    "infra_step": true,
-    "name": "makedirs test"
-  },
-  {
-    "cmd": [
-      "/opt/infra-android/tools/adb",
-      "shell",
-      "rm",
-      "-rf",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "rm /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "/opt/infra-android/tools/adb",
-      "shell",
-      "mkdir",
-      "-p",
-      "/sdcard/revenge_of_the_skiabot/dm_out"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "mkdir /sdcard/revenge_of_the_skiabot/dm_out"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = sys.argv[1]\nRETRIES = 5\nTIMEOUT = 60\nWAIT_BASE = 15\n\nsocket.setdefaulttimeout(TIMEOUT)\nfor retry in range(RETRIES):\n  try:\n    with contextlib.closing(\n        urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:\n      hashes = w.read()\n      with open(sys.argv[2], 'w') as f:\n        f.write(hashes)\n        break\n  except Exception as e:\n    print 'Failed to get uninteresting hashes from %s:' % HASHES_URL\n    print e\n    if retry == RETRIES:\n      raise\n    waittime = WAIT_BASE * math.pow(2, retry)\n    print 'Retry in %d seconds.' % waittime\n    time.sleep(waittime)\n",
-      "https://example.com/hashes.txt",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "get uninteresting hashes",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import contextlib@@@",
-      "@@@STEP_LOG_LINE@python.inline@import math@@@",
-      "@@@STEP_LOG_LINE@python.inline@import socket@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@HASHES_URL = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@RETRIES = 5@@@",
-      "@@@STEP_LOG_LINE@python.inline@TIMEOUT = 60@@@",
-      "@@@STEP_LOG_LINE@python.inline@WAIT_BASE = 15@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@socket.setdefaulttimeout(TIMEOUT)@@@",
-      "@@@STEP_LOG_LINE@python.inline@for retry in range(RETRIES):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    with contextlib.closing(@@@",
-      "@@@STEP_LOG_LINE@python.inline@        urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      hashes = w.read()@@@",
-      "@@@STEP_LOG_LINE@python.inline@      with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        f.write(hashes)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except Exception as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Failed to get uninteresting hashes from %s:' % HASHES_URL@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print e@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if retry == RETRIES:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@    waittime = WAIT_BASE * math.pow(2, retry)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Retry in %d seconds.' % waittime@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(waittime)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "/opt/infra-android/tools/adb",
       "push",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "/sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/chrome_infrastructure_adbkey",
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "push [START_DIR]/tmp/uninteresting_hashes.txt /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id (2)",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_BOT_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_TASK_ID', '')\n"
-    ],
-    "name": "get swarming task id",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_TASK_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "/opt/infra-android/tools/adb",
-      "push",
-      "[START_DIR]/build/dm",
+      "[START_DIR]/build/nanobench",
       "/data/local/tmp/"
     ],
     "cwd": "[START_DIR]/skia",
@@ -887,7 +1045,7 @@
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
     "infra_step": true,
-    "name": "push dm"
+    "name": "push nanobench"
   },
   {
     "cmd": [
@@ -897,25 +1055,21 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "set -x; /data/local/tmp/dm --resourcePath /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/dm --colorImages /sdcard/revenge_of_the_skiabot/images/colorspace --nameByHash --properties gitHash abc123 builder Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-All-Android buildbucket_build_id 123454321 task_id task_12345 swarming_bot_id \"\" swarming_task_id \"\" --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Tegra3 extra_config Android model Nexus7 os Android style default --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --dont_write pdf --threads 0 --nocpu --config gles glesdft glessrgb --src tests gm image colorImage svg --blacklist _ test _ ProcessorCloneTest _ test _ Programs _ test _ ProcessorOptimizationValidationTest _ gm _ savelayer_clipmask _ svg _ svgparse_ glessrgb image _ _ _ image gen_platf error _ test _ GrShape _ test _ SRGBReadWritePixels _ test _ SRGBMipMap _ test _ CharacterizationBackendAllocationTest _ test _ ColorTypeBackendAllocationTest _ test _ GLBackendAllocationTest _ test _ VKBackendAllocationTest _ image _ interlaced1.png _ image _ interlaced2.png _ image _ interlaced3.png _ image _ .arw _ image _ .cr2 _ image _ .dng _ image _ .nef _ image _ .nrw _ image _ .orf _ image _ .raf _ image _ .rw2 _ image _ .pef _ image _ .srw _ image _ .ARW _ image _ .CR2 _ image _ .DNG _ image _ .NEF _ image _ .NRW _ image _ .ORF _ image _ .RAF _ image _ .RW2 _ image _ .PEF _ image _ .SRW --nonativeFonts --verbose; echo $? >/data/local/tmp/rc",
-      "[START_DIR]/tmp/dm.sh"
+      "set -x; /data/local/tmp/nanobench --some-flag; echo $? >/data/local/tmp/rc",
+      "[START_DIR]/tmp/nanobench.sh"
     ],
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
     "infra_step": true,
-    "name": "write dm.sh",
+    "name": "write nanobench.sh",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@dm.sh@set -x; /data/local/tmp/dm --resourcePath /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/dm --colorImages /sdcard/revenge_of_the_skiabot/images/colorspace --nameByHash --properties gitHash abc123 builder Test-Android-Clang-Nexus7-GPU-Tegra3-arm-Debug-All-Android buildbucket_build_id 123454321 task_id task_12345 swarming_bot_id \"\" swarming_task_id \"\" --svgs /sdcard/revenge_of_the_skiabot/svgs --key arch arm compiler Clang configuration Debug cpu_or_gpu GPU cpu_or_gpu_value Tegra3 extra_config Android model Nexus7 os Android style default --uninterestingHashesFile /sdcard/revenge_of_the_skiabot/uninteresting_hashes.txt --writePath /sdcard/revenge_of_the_skiabot/dm_out --dont_write pdf --threads 0 --nocpu --config gles glesdft glessrgb --src tests gm image colorImage svg --blacklist _ test _ ProcessorCloneTest _ test _ Programs _ test _ ProcessorOptimizationValidationTest _ gm _ savelayer_clipmask _ svg _ svgparse_ glessrgb image _ _ _ image gen_platf error _ test _ GrShape _ test _ SRGBReadWritePixels _ test _ SRGBMipMap _ test _ CharacterizationBackendAllocationTest _ test _ ColorTypeBackendAllocationTest _ test _ GLBackendAllocationTest _ test _ VKBackendAllocationTest _ image _ interlaced1.png _ image _ interlaced2.png _ image _ interlaced3.png _ image _ .arw _ image _ .cr2 _ image _ .dng _ image _ .nef _ image _ .nrw _ image _ .orf _ image _ .raf _ image _ .rw2 _ image _ .pef _ image _ .srw _ image _ .ARW _ image _ .CR2 _ image _ .DNG _ image _ .NEF _ image _ .NRW _ image _ .ORF _ image _ .RAF _ image _ .RW2 _ image _ .PEF _ image _ .SRW --nonativeFonts --verbose; echo $? >/data/local/tmp/rc@@@",
-      "@@@STEP_LOG_END@dm.sh@@@"
+      "@@@STEP_LOG_LINE@nanobench.sh@set -x; /data/local/tmp/nanobench --some-flag; echo $? >/data/local/tmp/rc@@@",
+      "@@@STEP_LOG_END@nanobench.sh@@@"
     ]
   },
   {
     "cmd": [
       "/opt/infra-android/tools/adb",
       "push",
-      "[START_DIR]/tmp/dm.sh",
+      "[START_DIR]/tmp/nanobench.sh",
       "/data/local/tmp/"
     ],
     "cwd": "[START_DIR]/skia",
@@ -925,7 +1079,7 @@
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
     "infra_step": true,
-    "name": "push dm.sh"
+    "name": "push nanobench.sh"
   },
   {
     "cmd": [
@@ -948,13 +1102,9 @@
       "-u",
       "\nimport subprocess\nimport sys\nbin_dir = sys.argv[1]\nsh      = sys.argv[2]\nsubprocess.check_call(['/opt/infra-android/tools/adb', 'shell', 'sh', bin_dir + sh])\ntry:\n  sys.exit(int(subprocess.check_output(['/opt/infra-android/tools/adb', 'shell', 'cat',\n                                        bin_dir + 'rc'])))\nexcept ValueError:\n  print \"Couldn't read the return code.  Probably killed for OOM.\"\n  sys.exit(1)\n",
       "/data/local/tmp/",
-      "dm.sh"
+      "nanobench.sh"
     ],
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "dm",
+    "name": "nanobench",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
@@ -979,7 +1129,7 @@
     "cmd": [
       "/opt/infra-android/tools/adb",
       "pull",
-      "/sdcard/revenge_of_the_skiabot/dm_out",
+      "/sdcard/revenge_of_the_skiabot/perf",
       "[CLEANUP]/adb_pull_tmp_1"
     ],
     "cwd": "[START_DIR]/skia",
@@ -989,7 +1139,7 @@
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
     "infra_step": true,
-    "name": "adb pull.pull /sdcard/revenge_of_the_skiabot/dm_out",
+    "name": "adb pull.pull /sdcard/revenge_of_the_skiabot/perf",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@"
     ]
@@ -1003,7 +1153,7 @@
       "/path/to/tmp/json",
       "glob",
       "[CLEANUP]/adb_pull_tmp_1",
-      "dm_out/*"
+      "perf/*"
     ],
     "infra_step": true,
     "name": "adb pull.list pulled files",
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
index 378e2fd..40065ad 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
index 7c768d7..acf2a37 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
@@ -152,7 +152,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get mskp VERSION"
+    "name": "Get mskp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
index 931b3cf..eaa5c81 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -240,7 +244,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -380,7 +388,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -520,7 +532,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
index d0efb68..aab78e0 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
@@ -232,7 +232,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -378,7 +382,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -524,7 +532,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
index 5d72402..bf8a9ce 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json
index 33436e0..4ae4d3c 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-UBSAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-UBSAN.json
index e8df83e..d515f9e 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-UBSAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-UBSAN.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
index 6398009..a8a4bf9 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
index a7da2e5..5658961 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
index 06609f7..8c35de6 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json
index a2dbb24..3fa0196 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json
@@ -325,7 +325,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -523,7 +527,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -721,7 +729,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json
index 860d9f1..b3cda5b 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
index bf4ae71..58a3877 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -240,7 +244,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -380,7 +388,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -520,7 +532,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
copy to infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json
index 8672bbe..0b7a600 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -179,23 +194,86 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
-      "[START_DIR]/valgrind/bin/valgrind",
-      "--gen-suppressions=all",
-      "--leak-check=full",
-      "--track-origins=yes",
-      "--error-exitcode=1",
-      "--num-callers=40",
-      "--suppressions=[START_DIR]/skia/tools/valgrind.supp",
-      "[START_DIR]/build/dm",
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
+      "gcr.io/skia-public/gcc-debian10-x86@sha256:b1ec55403ac66d9500d033d6ffd7663894d32335711fbbb0fb4c67dfce812203",
+      "/SRC/../RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "/SRC",
+      "catchsegv",
+      "/SRC/build/dm",
       "--some-flag"
     ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]",
-      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "dm"
+    "name": "symbolized dm in Docker"
   },
   {
     "name": "$result"
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
similarity index 62%
copy from infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
copy to infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
index 8672bbe..bb855cd 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -179,23 +194,86 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
-      "[START_DIR]/valgrind/bin/valgrind",
-      "--gen-suppressions=all",
-      "--leak-check=full",
-      "--track-origins=yes",
-      "--error-exitcode=1",
-      "--num-callers=40",
-      "--suppressions=[START_DIR]/skia/tools/valgrind.supp",
-      "[START_DIR]/build/dm",
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
+      "gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c",
+      "/SRC/../RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "/SRC",
+      "catchsegv",
+      "/SRC/build/dm",
       "--some-flag"
     ],
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]",
-      "VALGRIND_LIB": "[START_DIR]/valgrind/lib/valgrind"
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "dm"
+    "name": "symbolized dm in Docker"
   },
   {
     "name": "$result"
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json
index abd35fbf..bc2c72c 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
index c472881..baa6cd3 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index e206d29..570f679 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json
index aef4a06..ccedf1a 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json
index c3fde7f..9284aaa 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
index bb11ed7..86ea666 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json
index 0d0ca2a..09c58b4 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
similarity index 89%
rename from infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
rename to infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
index 8672bbe..4fc94c9 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json
index 6aadb47..1b4148d 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json
@@ -11,7 +11,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read file.txt"
+    "name": "read file.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@file.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -94,7 +97,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -126,7 +133,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -158,7 +169,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
index ecf9631..b8a67eb 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -60,7 +64,8 @@
     "infra_step": true,
     "name": "read file.txt.read file.txt",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@file.txt@@@"
     ]
   },
   {
@@ -274,7 +279,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -328,7 +337,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SKP_VERSION.read SKP_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SKP_VERSION@@@"
     ]
   },
   {
@@ -447,7 +457,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -501,7 +515,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SK_IMAGE_VERSION.read SK_IMAGE_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SK_IMAGE_VERSION@@@"
     ]
   },
   {
@@ -620,7 +635,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -674,7 +693,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SVG_VERSION.read SVG_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SVG_VERSION@@@"
     ]
   },
   {
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json
index 7e90002..1fd855e 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_golo.json b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_golo.json
index 794c19d..a9fafad 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_golo.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_golo.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_once.json b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_once.json
index c427fa9..cb16ba6 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_once.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/cpu_scale_failed_once.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/failed_infra_step.json b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_infra_step.json
index 042bbeb..63ed427 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/failed_infra_step.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_infra_step.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -631,7 +639,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/failed_read_version.json b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_read_version.json
index 57fd360..32b242b 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/failed_read_version.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/failed_read_version.json
@@ -235,7 +235,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -433,7 +437,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -680,7 +688,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/retry_adb_command.json b/infra/bots/recipe_modules/flavor/examples/full.expected/retry_adb_command.json
index 5bca9a19..9ecfd7c 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/retry_adb_command.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/retry_adb_command.json
@@ -285,7 +285,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -483,7 +487,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -681,7 +689,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.expected/retry_ios_install.json b/infra/bots/recipe_modules/flavor/examples/full.expected/retry_ios_install.json
index 6516d9e..963a8ec 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.expected/retry_ios_install.json
+++ b/infra/bots/recipe_modules/flavor/examples/full.expected/retry_ios_install.json
@@ -179,7 +179,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -285,7 +289,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -391,7 +399,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/flavor/examples/full.py b/infra/bots/recipe_modules/flavor/examples/full.py
index 0ebaefb..7374926 100644
--- a/infra/bots/recipe_modules/flavor/examples/full.py
+++ b/infra/bots/recipe_modules/flavor/examples/full.py
@@ -46,6 +46,8 @@
         api.flavor.install(lotties=True)
       elif 'Mskp' in api.properties['buildername']:
         api.flavor.install(mskps=True)
+      elif all(v in api.properties['buildername'] for v in ['Perf', 'Android', 'CPU']):
+        api.flavor.install(skps=True, images=True, svgs=True, resources=True, texttraces=True)
       else:
         api.flavor.install(skps=True, images=True, lotties=False, svgs=True,
                            resources=True)
@@ -69,6 +71,7 @@
 TEST_BUILDERS = [
   'Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing',
   'Perf-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android',
+  'Perf-Android-Clang-NVIDIA_Shield-CPU-TegraX1-arm64-Release-All-Android',
   'Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android',
   'Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp',
   'Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All',
@@ -82,6 +85,8 @@
   'Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN',
   'Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan',
   'Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All',
+  'Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker',
+  'Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker',
   'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage',
   'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie',
   'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN',
@@ -89,7 +94,7 @@
   'Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL',
   'Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan',
   'Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN',
-  ('Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All'
+  ('Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All'
    '-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41'),
   'Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump',
   'Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE',
@@ -119,7 +124,7 @@
           stdout=api.raw_io.output('192.168.1.2:5555'))
     yield test
 
-  builder = 'Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All'
+  builder = 'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All'
   yield (
       api.test('exceptions') +
       api.properties(buildername=builder,
diff --git a/infra/bots/recipe_modules/flavor/ios.py b/infra/bots/recipe_modules/flavor/ios.py
index e56d089..c329fa7 100644
--- a/infra/bots/recipe_modules/flavor/ios.py
+++ b/infra/bots/recipe_modules/flavor/ios.py
@@ -25,7 +25,8 @@
         skp_dir='skps',
         svg_dir='svgs',
         mskp_dir='mskp',
-        tmp_dir='tmp')
+        tmp_dir='tmp',
+        texttraces_dir='')
 
   def install(self):
     # Set up the device
diff --git a/infra/bots/recipe_modules/flavor/resources/win_ssh_cmd.py b/infra/bots/recipe_modules/flavor/resources/win_ssh_cmd.py
old mode 100644
new mode 100755
diff --git a/infra/bots/recipe_modules/flavor/win_ssh.py b/infra/bots/recipe_modules/flavor/win_ssh.py
index 0db0429..8ac273c 100644
--- a/infra/bots/recipe_modules/flavor/win_ssh.py
+++ b/infra/bots/recipe_modules/flavor/win_ssh.py
@@ -23,16 +23,17 @@
     super(WinSSHFlavor, self).__init__(m)
     self.remote_homedir = 'C:\\Users\\chrome-bot\\botdata\\'
     self.device_dirs = default.DeviceDirs(
-      bin_dir       = self.device_path_join(self.remote_homedir, 'bin'),
-      dm_dir        = self.device_path_join(self.remote_homedir, 'dm_out'),
-      perf_data_dir = self.device_path_join(self.remote_homedir, 'perf'),
-      resource_dir  = self.device_path_join(self.remote_homedir, 'resources'),
-      images_dir    = self.device_path_join(self.remote_homedir, 'images'),
-      lotties_dir   = self.device_path_join(self.remote_homedir, 'lotties'),
-      skp_dir       = self.device_path_join(self.remote_homedir, 'skps'),
-      svg_dir       = self.device_path_join(self.remote_homedir, 'svgs'),
-      mskp_dir      = self.device_path_join(self.remote_homedir, 'mskp'),
-      tmp_dir       = self.remote_homedir)
+      bin_dir        = self.device_path_join(self.remote_homedir, 'bin'),
+      dm_dir         = self.device_path_join(self.remote_homedir, 'dm_out'),
+      perf_data_dir  = self.device_path_join(self.remote_homedir, 'perf'),
+      resource_dir   = self.device_path_join(self.remote_homedir, 'resources'),
+      images_dir     = self.device_path_join(self.remote_homedir, 'images'),
+      lotties_dir    = self.device_path_join(self.remote_homedir, 'lotties'),
+      skp_dir        = self.device_path_join(self.remote_homedir, 'skps'),
+      svg_dir        = self.device_path_join(self.remote_homedir, 'svgs'),
+      mskp_dir       = self.device_path_join(self.remote_homedir, 'mskp'),
+      tmp_dir        = self.remote_homedir,
+      texttraces_dir = '')
     self._empty_dir = self.device_path_join(self.remote_homedir, 'empty')
 
 
diff --git a/infra/bots/recipe_modules/run/examples/full.expected/test.json b/infra/bots/recipe_modules/run/examples/full.expected/test.json
index 162abfd..9b446df 100644
--- a/infra/bots/recipe_modules/run/examples/full.expected/test.json
+++ b/infra/bots/recipe_modules/run/examples/full.expected/test.json
@@ -59,7 +59,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read myfile.txt"
+    "name": "read myfile.txt",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@myfile.txt@@@"
+    ]
   },
   {
     "cmd": [
@@ -104,7 +107,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get my_asset VERSION"
+    "name": "Get my_asset VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipe_modules/vars/api.py b/infra/bots/recipe_modules/vars/api.py
index a23b326..182864a 100644
--- a/infra/bots/recipe_modules/vars/api.py
+++ b/infra/bots/recipe_modules/vars/api.py
@@ -17,6 +17,13 @@
 
   def setup(self):
     """Prepare the variables."""
+    # Hack start_dir to remove the "k" directory which is added by Kitchen.
+    # Otherwise, we can't get to the CIPD packages, caches, and isolates which
+    # were put into the task workdir.
+    if self.m.path.c.base_paths['start_dir'][-1] == 'k':  # pragma: nocover
+      self.m.path.c.base_paths['start_dir'] = (
+          self.m.path.c.base_paths['start_dir'][:-1])
+
     # Setup
     self.builder_name = self.m.properties['buildername']
 
@@ -41,8 +48,7 @@
     self.builder_cfg = self.m.builder_name_schema.DictForBuilderName(
         self.builder_name)
     self.role = self.builder_cfg['role']
-    if self.role in [self.m.builder_name_schema.BUILDER_ROLE_HOUSEKEEPER,
-                     self.m.builder_name_schema.BUILDER_ROLE_CALMBENCH]:
+    if self.role == self.m.builder_name_schema.BUILDER_ROLE_HOUSEKEEPER:
       self.configuration = CONFIG_RELEASE
     else:
       self.configuration = self.builder_cfg.get('configuration', CONFIG_DEBUG)
@@ -81,7 +87,11 @@
 
   @property
   def is_linux(self):
-    return 'Ubuntu' in self.builder_name or 'Debian' in self.builder_name
+    return (
+        'Ubuntu' in self.builder_name
+     or 'Debian' in self.builder_name
+     or 'Housekeeper' in self.builder_name
+    )
 
   @property
   def swarming_bot_id(self):
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
index 1ac7650..09c14c8 100644
--- a/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -37,7 +37,7 @@
       "@@@SET_BUILD_PROPERTY@extra_tokens@\"['RecreateSKPs']\"@@@",
       "@@@SET_BUILD_PROPERTY@internal_hardware_label@\"None\"@@@",
       "@@@SET_BUILD_PROPERTY@is_internal_bot@\"False\"@@@",
-      "@@@SET_BUILD_PROPERTY@is_linux@\"False\"@@@",
+      "@@@SET_BUILD_PROPERTY@is_linux@\"True\"@@@",
       "@@@SET_BUILD_PROPERTY@is_trybot@\"False\"@@@",
       "@@@SET_BUILD_PROPERTY@issue@\"None\"@@@",
       "@@@SET_BUILD_PROPERTY@patch_storage@\"gerrit\"@@@",
diff --git a/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json b/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json
index 87c23d4..06e2078 100644
--- a/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json
+++ b/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json
@@ -30,8 +30,8 @@
     "name": "show",
     "~followup_annotations": [
       "@@@SET_BUILD_PROPERTY@build_dir@\"[START_DIR]/build\"@@@",
-      "@@@SET_BUILD_PROPERTY@builder_cfg@\"{'extra_config': 'ASAN_Vulkan', 'cpu_or_gpu_value': 'AVX2', 'arch': 'x86_64', 'test_filter': 'All', 'sub-role-1': 'Test', 'cpu_or_gpu': 'CPU', 'role': 'Upload', 'model': 'GCE', 'configuration': 'Debug', 'os': 'Debian9', 'compiler': 'GCC'}\"@@@",
-      "@@@SET_BUILD_PROPERTY@builder_name@\"Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All-ASAN_Vulkan\"@@@",
+      "@@@SET_BUILD_PROPERTY@builder_cfg@\"{'extra_config': 'ASAN_Vulkan', 'cpu_or_gpu_value': 'AVX2', 'arch': 'x86_64', 'test_filter': 'All', 'sub-role-1': 'Test', 'cpu_or_gpu': 'CPU', 'role': 'Upload', 'model': 'GCE', 'configuration': 'Debug', 'os': 'Debian9', 'compiler': 'Clang'}\"@@@",
+      "@@@SET_BUILD_PROPERTY@builder_name@\"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN_Vulkan\"@@@",
       "@@@SET_BUILD_PROPERTY@cache_dir@\"[START_DIR]/cache\"@@@",
       "@@@SET_BUILD_PROPERTY@default_env@\"{'PATH': '%(PATH)s:RECIPE_REPO[depot_tools]', 'CHROME_HEADLESS': '1'}\"@@@",
       "@@@SET_BUILD_PROPERTY@extra_tokens@\"['ASAN', 'Vulkan']\"@@@",
diff --git a/infra/bots/recipe_modules/vars/examples/full.py b/infra/bots/recipe_modules/vars/examples/full.py
index 1fded69..01d8efd 100644
--- a/infra/bots/recipe_modules/vars/examples/full.py
+++ b/infra/bots/recipe_modules/vars/examples/full.py
@@ -77,7 +77,7 @@
       )
   )
 
-  buildername = 'Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All-ASAN_Vulkan'
+  buildername = 'Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN_Vulkan'
   yield (
       api.test('integer_issue') +
       api.properties(buildername=buildername,
diff --git a/infra/bots/recipes/android_compile.expected/android_compile_nontrybot.json b/infra/bots/recipes/android_compile.expected/android_compile_nontrybot.json
index 9301649..c75f720 100644
--- a/infra/bots/recipes/android_compile.expected/android_compile_nontrybot.json
+++ b/infra/bots/recipes/android_compile.expected/android_compile_nontrybot.json
@@ -15,7 +15,7 @@
       "    arg_names, **additional_args)",
       "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
       "    return callable_obj(*props, **additional_args)",
-      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/android_compile.py\", line 36, in RunSteps",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/android_compile.py\", line 37, in RunSteps",
       "    raise Exception('%s can only be run as a trybot.' % api.vars.builder_name)",
       "Exception: Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework can only be run as a trybot."
     ]
diff --git a/infra/bots/recipes/android_compile.expected/android_compile_sdk_trybot.json b/infra/bots/recipes/android_compile.expected/android_compile_sdk_trybot.json
index c77f008..5b3729b 100644
--- a/infra/bots/recipes/android_compile.expected/android_compile_sdk_trybot.json
+++ b/infra/bots/recipes/android_compile.expected/android_compile_sdk_trybot.json
@@ -10,8 +10,15 @@
       "--issue",
       "456789",
       "--patchset",
-      "12"
+      "12",
+      "--builder_name",
+      "Build-Debian9-Clang-host-sdk-Android_Framework"
     ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
     "name": "Trigger and wait for task on android compile server"
   },
   {
diff --git a/infra/bots/recipes/android_compile.expected/android_compile_trybot.json b/infra/bots/recipes/android_compile.expected/android_compile_trybot.json
index 90f43ef..a01d933 100644
--- a/infra/bots/recipes/android_compile.expected/android_compile_trybot.json
+++ b/infra/bots/recipes/android_compile.expected/android_compile_trybot.json
@@ -10,8 +10,15 @@
       "--issue",
       "456789",
       "--patchset",
-      "12"
+      "12",
+      "--builder_name",
+      "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework"
     ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
     "name": "Trigger and wait for task on android compile server"
   },
   {
diff --git a/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json b/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json
index 7425844..7e9255d 100644
--- a/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json
+++ b/infra/bots/recipes/android_compile.expected/android_compile_trybot_failure.json
@@ -10,8 +10,15 @@
       "--issue",
       "456789",
       "--patchset",
-      "12"
+      "12",
+      "--builder_name",
+      "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework"
     ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
     "name": "Trigger and wait for task on android compile server",
     "~followup_annotations": [
       "@@@STEP_FAILURE@@@"
diff --git a/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json b/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json
index 4b71020..e08ba3f 100644
--- a/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json
+++ b/infra/bots/recipes/android_compile.expected/android_compile_unrecognized_target.json
@@ -15,7 +15,7 @@
       "    arg_names, **additional_args)",
       "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
       "    return callable_obj(*props, **additional_args)",
-      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/android_compile.py\", line 46, in RunSteps",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/android_compile.py\", line 47, in RunSteps",
       "    api.vars.builder_name)",
       "Exception: Lunch target in Build-Debian9-Clang-unrecognized-Android_Framework is not recognized."
     ]
diff --git a/infra/bots/recipes/android_compile.py b/infra/bots/recipes/android_compile.py
index fc982b9..fb643bc 100644
--- a/infra/bots/recipes/android_compile.py
+++ b/infra/bots/recipes/android_compile.py
@@ -13,6 +13,7 @@
   'recipe_engine/properties',
   'recipe_engine/raw_io',
   'recipe_engine/step',
+  'run',
   'vars',
 ]
 
@@ -56,9 +57,11 @@
          '--mmma_targets', mmma_targets,
          '--issue', api.vars.issue,
          '--patchset', api.vars.patchset,
+         '--builder_name', api.vars.builder_name,
         ]
   try:
-    api.step('Trigger and wait for task on android compile server', cmd=cmd)
+    with api.context(cwd=api.path['start_dir'].join('skia')):
+      api.run(api.step, 'Trigger and wait for task on android compile server', cmd=cmd)
   except api.step.StepFailure as e:
     # Add withpatch and nopatch logs as links (if they exist).
     gs_file = 'gs://android-compile-tasks/%s-%s-%s.json' % (
diff --git a/infra/bots/recipes/calmbench.expected/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All.json b/infra/bots/recipes/calmbench.expected/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All.json
deleted file mode 100644
index 8271346..0000000
--- a/infra/bots/recipes/calmbench.expected/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All.json
+++ /dev/null
@@ -1,139 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get skp VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SKP_VERSION@42@@@",
-      "@@@STEP_LOG_END@SKP_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get svg VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SVG_VERSION@42@@@",
-      "@@@STEP_LOG_END@SVG_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "makedirs perf"
-  },
-  {
-    "cmd": [
-      "python",
-      "[START_DIR]/skia/tools/calmbench/ab.py",
-      "[START_DIR]/[SWARM_OUT_DIR]",
-      "modified",
-      "master",
-      "[START_DIR]/build/nanobench",
-      "[START_DIR]/build/ParentRevision/nanobench",
-      "--svgs [START_DIR]/svg --skps [START_DIR]/skp --mpd false",
-      "--svgs [START_DIR]/svg --skps [START_DIR]/skp --mpd false",
-      "2",
-      "false",
-      "8888",
-      "-1",
-      "false",
-      "--githash",
-      "abc123",
-      "--concise",
-      "--keys",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "CPU",
-      "cpu_or_gpu_value",
-      "AVX2",
-      "model",
-      "GCE",
-      "os",
-      "Debian9"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "Run calmbench"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/calmbench.expected/Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All.json b/infra/bots/recipes/calmbench.expected/Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All.json
deleted file mode 100644
index 25056fa..0000000
--- a/infra/bots/recipes/calmbench.expected/Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All.json
+++ /dev/null
@@ -1,139 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get skp VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SKP_VERSION@42@@@",
-      "@@@STEP_LOG_END@SKP_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get svg VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SVG_VERSION@42@@@",
-      "@@@STEP_LOG_END@SVG_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "makedirs perf"
-  },
-  {
-    "cmd": [
-      "python",
-      "[START_DIR]/skia/tools/calmbench/ab.py",
-      "[START_DIR]/[SWARM_OUT_DIR]",
-      "modified",
-      "master",
-      "[START_DIR]/build/nanobench",
-      "[START_DIR]/build/ParentRevision/nanobench",
-      "--svgs [START_DIR]/svg --skps [START_DIR]/skp",
-      "--svgs [START_DIR]/svg --skps [START_DIR]/skp",
-      "2",
-      "false",
-      "gl",
-      "-1",
-      "false",
-      "--githash",
-      "abc123",
-      "--concise",
-      "--keys",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "QuadroP400",
-      "model",
-      "Golo",
-      "os",
-      "Ubuntu17"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "Run calmbench"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/calmbench.py b/infra/bots/recipes/calmbench.py
deleted file mode 100644
index c96a082..0000000
--- a/infra/bots/recipes/calmbench.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-# Recipe module for Skia Swarming calmbench.
-
-DEPS = [
-  'flavor',
-  'recipe_engine/context',
-  'recipe_engine/file',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/python',
-  'recipe_engine/raw_io',
-  'recipe_engine/step',
-  'recipe_engine/time',
-  'run',
-  'vars',
-]
-
-def RunSteps(api):
-  api.vars.setup()
-  api.file.ensure_directory('makedirs tmp_dir', api.vars.tmp_dir)
-  api.flavor.setup()
-
-  api.flavor.install(skps=True, svgs=True)
-  api.file.ensure_directory('makedirs perf', api.vars.swarming_out_dir)
-
-  skia_dir = api.path['start_dir'].join('skia')
-  with api.context(cwd=skia_dir):
-    extra_arg = '--svgs %s --skps %s' % (api.flavor.device_dirs.svg_dir,
-                                         api.flavor.device_dirs.skp_dir)
-
-    # measuring multi-picture-draw in our multi-threaded CPU test is inaccurate
-    if api.vars.builder_cfg.get('cpu_or_gpu') == 'CPU':
-      extra_arg += ' --mpd false'
-      config = "8888"
-    else:
-      config = "gl"
-
-    command = [
-        'python',
-        skia_dir.join('tools', 'calmbench', 'ab.py'),
-        api.vars.swarming_out_dir,
-        'modified', 'master',
-        api.vars.build_dir.join('nanobench'),
-        api.vars.build_dir.join('ParentRevision', 'nanobench'),
-        extra_arg, extra_arg,
-        2,          # reps
-        "false",    # skipbase
-        config,
-        -1,         # threads; let ab.py decide the threads
-        "false",    # noinit
-        "--githash", api.properties['revision'],
-        "--concise"
-    ]
-
-    keys_blacklist = ['configuration', 'role', 'test_filter']
-    command.append('--keys')
-    for k in sorted(api.vars.builder_cfg.keys()):
-      if not k in keys_blacklist:
-        command.extend([k, api.vars.builder_cfg[k]])
-
-    api.run(api.step, 'Run calmbench', cmd=command)
-  api.run.check_failure()
-
-def GenTests(api):
-  builders = [
-    "Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All",
-    "Calmbench-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Release-All",
-  ]
-
-  for builder in builders:
-    test = (
-      api.test(builder) +
-      api.properties(buildername=builder,
-                     repository='https://skia.googlesource.com/skia.git',
-                     revision='abc123',
-                     path_config='kitchen',
-                     swarm_out_dir='[SWARM_OUT_DIR]') +
-      api.path.exists(
-          api.path['start_dir'].join('skia'),
-          api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                     'svg', 'VERSION'),
-          api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                     'skp', 'VERSION'),
-      )
-    )
-
-    yield test
diff --git a/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json b/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
index 93e8569..5672fbc 100644
--- a/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
+++ b/infra/bots/recipes/check_generated_files.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
@@ -231,6 +256,25 @@
   },
   {
     "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
+      "/path/to/tmp/"
+    ],
+    "cwd": "[START_DIR]/cache/work/skia",
+    "infra_step": true,
+    "name": "Get clang_linux VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
       "python",
       "-u",
       "[START_DIR]/cache/work/skia/bin/fetch-gn"
@@ -262,7 +306,7 @@
       "[START_DIR]/cache/work/skia/bin/gn",
       "gen",
       "[START_DIR]/build/out/Release",
-      "--args=is_debug=false skia_compile_processors=true skia_generate_workarounds=true werror=true"
+      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false skia_compile_processors=true skia_generate_workarounds=true werror=true"
     ],
     "cwd": "[START_DIR]/cache/work/skia",
     "env": {
diff --git a/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json b/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json
index 0e33dbe..bb73f32 100644
--- a/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json
+++ b/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json
@@ -11,7 +11,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_win VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/compute_buildstats.expected/normal_bot.json b/infra/bots/recipes/compute_buildstats.expected/normal_bot.json
index d934e0d..1637d86 100644
--- a/infra/bots/recipes/compute_buildstats.expected/normal_bot.json
+++ b/infra/bots/recipes/compute_buildstats.expected/normal_bot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/compute_buildstats.expected/trybot.json b/infra/bots/recipes/compute_buildstats.expected/trybot.json
index 46a1769..b8a8917 100644
--- a/infra/bots/recipes/compute_buildstats.expected/trybot.json
+++ b/infra/bots/recipes/compute_buildstats.expected/trybot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/g3_compile.expected/g3_compile_nontrybot.json b/infra/bots/recipes/g3_compile.expected/g3_compile_nontrybot.json
index e2442c8..45e39d0 100644
--- a/infra/bots/recipes/g3_compile.expected/g3_compile_nontrybot.json
+++ b/infra/bots/recipes/g3_compile.expected/g3_compile_nontrybot.json
@@ -15,7 +15,7 @@
       "    arg_names, **additional_args)",
       "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
       "    return callable_obj(*props, **additional_args)",
-      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/g3_compile.py\", line 20, in RunSteps",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/g3_compile.py\", line 22, in RunSteps",
       "    raise Exception('%s can only be run as a trybot.' % api.vars.builder_name)",
       "Exception: Build-Debian9-Clang-TAP-Presubmit-G3_Framework can only be run as a trybot."
     ]
diff --git a/infra/bots/recipes/g3_compile.expected/g3_compile_trybot.json b/infra/bots/recipes/g3_compile.expected/g3_compile_trybot.json
index 8562d56..22d8846 100644
--- a/infra/bots/recipes/g3_compile.expected/g3_compile_trybot.json
+++ b/infra/bots/recipes/g3_compile.expected/g3_compile_trybot.json
@@ -8,8 +8,15 @@
       "--patchset",
       "12",
       "--output_file",
-      "[CLEANUP]/g3_try_tmp_1/output_file"
+      "[CLEANUP]/g3_try_tmp_1/output_file",
+      "--builder_name",
+      "Build-Debian9-Clang-TAP-Presubmit-G3_Framework"
     ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
     "name": "Trigger and wait for g3 compile task"
   },
   {
diff --git a/infra/bots/recipes/g3_compile.expected/g3_compile_trybot_failure.json b/infra/bots/recipes/g3_compile.expected/g3_compile_trybot_failure.json
index 8d15861..dd97c86 100644
--- a/infra/bots/recipes/g3_compile.expected/g3_compile_trybot_failure.json
+++ b/infra/bots/recipes/g3_compile.expected/g3_compile_trybot_failure.json
@@ -8,8 +8,15 @@
       "--patchset",
       "12",
       "--output_file",
-      "[CLEANUP]/g3_try_tmp_1/output_file"
+      "[CLEANUP]/g3_try_tmp_1/output_file",
+      "--builder_name",
+      "Build-Debian9-Clang-TAP-Presubmit-G3_Framework"
     ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
     "name": "Trigger and wait for g3 compile task",
     "~followup_annotations": [
       "@@@STEP_FAILURE@@@"
@@ -29,6 +36,8 @@
     "infra_step": true,
     "name": "Read task json",
     "~followup_annotations": [
+      "@@@STEP_LOG_LINE@output_file@{\"cl\": 12345}@@@",
+      "@@@STEP_LOG_END@output_file@@@",
       "@@@STEP_LINK@CL link@http://cl/12345@@@"
     ]
   },
diff --git a/infra/bots/recipes/g3_compile.py b/infra/bots/recipes/g3_compile.py
index 1c320b6..b7256d3 100644
--- a/infra/bots/recipes/g3_compile.py
+++ b/infra/bots/recipes/g3_compile.py
@@ -4,12 +4,14 @@
 
 
 DEPS = [
+  'recipe_engine/context',
   'recipe_engine/file',
   'recipe_engine/json',
   'recipe_engine/path',
   'recipe_engine/properties',
   'recipe_engine/raw_io',
   'recipe_engine/step',
+  'run',
   'vars',
 ]
 
@@ -30,9 +32,11 @@
          '--issue', api.vars.issue,
          '--patchset', api.vars.patchset,
          '--output_file', output_file,
+         '--builder_name', api.vars.builder_name,
         ]
   try:
-    api.step('Trigger and wait for g3 compile task', cmd=cmd)
+    with api.context(cwd=api.path['start_dir'].join('skia')):
+      api.run(api.step, 'Trigger and wait for g3 compile task', cmd=cmd)
   except api.step.StepFailure as e:
     # Add CL link if it exists in the output_file.
     task_json = api.file.read_json(
diff --git a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json
index e577cb6..d1587c4 100644
--- a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json
+++ b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit-Trybot.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json
index 0183395..d7e6558f 100644
--- a/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json
+++ b/infra/bots/recipes/housekeeper.expected/Housekeeper-PerCommit.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-All-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-All-Android.json
index 4ad1b56..0cf8bd5 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-All-Android.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-All-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-All-Android.json
index 1d9e20e..4090391 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-All-Android.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Debug-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_NoGPUThreads.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_NoGPUThreads.json
index a325df3..373abeb 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_NoGPUThreads.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_NoGPUThreads.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json
index 989cef7..b5248c6 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json
@@ -45,6 +45,208 @@
     "name": "mkdir /sdcard/revenge_of_the_skiabot/resources"
   },
   {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/skia/infra/bots/assets/text_blob_traces/VERSION",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "Get text_blob_traces VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "42",
+      "[START_DIR]/tmp/TEXTTRACES_VERSION"
+    ],
+    "infra_step": true,
+    "name": "write TEXTTRACES_VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@TEXTTRACES_VERSION@42@@@",
+      "@@@STEP_LOG_END@TEXTTRACES_VERSION@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "shell",
+      "cat",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "read /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "shell",
+      "rm",
+      "-f",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "shell",
+      "rm",
+      "-rf",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "rm /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "shell",
+      "mkdir",
+      "-p",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "mkdir /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [],
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "listdir",
+      "[START_DIR]/text_blob_traces",
+      "--recursive"
+    ],
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.list [START_DIR]/text_blob_traces",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/.file3@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/.ignore/file4@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/file1@@@",
+      "@@@STEP_LOG_LINE@listdir@[START_DIR]/text_blob_traces/subdir/file2@@@",
+      "@@@STEP_LOG_END@listdir@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "push",
+      "[START_DIR]/text_blob_traces/.file3",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/.file3"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/.file3",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "push",
+      "[START_DIR]/text_blob_traces/file1",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/file1"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/file1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "push",
+      "[START_DIR]/text_blob_traces/subdir/file2",
+      "/sdcard/revenge_of_the_skiabot/text_blob_traces/subdir/file2"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/text_blob_traces/* /sdcard/revenge_of_the_skiabot/text_blob_traces.push [START_DIR]/text_blob_traces/subdir/file2",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "/usr/bin/adb.1.0.35",
+      "push",
+      "[START_DIR]/tmp/TEXTTRACES_VERSION",
+      "/sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+    ],
+    "cwd": "[START_DIR]/skia",
+    "env": {
+      "ADB_VENDOR_KEYS": "/home/chrome-bot/.android/adbkey",
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "push [START_DIR]/tmp/TEXTTRACES_VERSION /sdcard/revenge_of_the_skiabot/TEXTTRACES_VERSION"
+  },
+  {
     "cmd": [],
     "name": "push [START_DIR]/skia/resources/* /sdcard/revenge_of_the_skiabot/resources"
   },
@@ -139,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +745,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -874,7 +1088,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "set -x; /data/local/tmp/nanobench -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --svgs /sdcard/revenge_of_the_skiabot/svgs --pre_log --scales 1.0 1.1 --nogpu --purgeBetweenBenches --config 8888 nonrendering --loops 1 --samples 1 --keepAlive true --match ~blurroundrect ~patch_grid ~desk_carsvg ~inc0.gif ~inc1.gif ~incInterlaced.gif ~inc0.jpg ~incGray.jpg ~inc0.wbmp ~inc1.wbmp ~inc0.webp ~inc1.webp ~inc0.ico ~inc1.ico ~inc0.png ~inc1.png ~inc2.png ~inc12.png ~inc13.png ~inc14.png ~inc0.webp ~inc1.webp; echo $? >/data/local/tmp/rc",
+      "set -x; /data/local/tmp/nanobench -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --svgs /sdcard/revenge_of_the_skiabot/svgs --pre_log --scales 1.0 1.1 --nogpu --purgeBetweenBenches --texttraces /sdcard/revenge_of_the_skiabot/text_blob_traces --config 8888 nonrendering --loops 1 --samples 1 --keepAlive true --match ~blurroundrect ~patch_grid ~desk_carsvg ~inc0.gif ~inc1.gif ~incInterlaced.gif ~inc0.jpg ~incGray.jpg ~inc0.wbmp ~inc1.wbmp ~inc0.webp ~inc1.webp ~inc0.ico ~inc1.ico ~inc0.png ~inc1.png ~inc2.png ~inc12.png ~inc13.png ~inc14.png ~inc0.webp ~inc1.webp; echo $? >/data/local/tmp/rc",
       "[START_DIR]/tmp/nanobench.sh"
     ],
     "env": {
@@ -884,7 +1098,7 @@
     "infra_step": true,
     "name": "write nanobench.sh",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@nanobench.sh@set -x; /data/local/tmp/nanobench -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --svgs /sdcard/revenge_of_the_skiabot/svgs --pre_log --scales 1.0 1.1 --nogpu --purgeBetweenBenches --config 8888 nonrendering --loops 1 --samples 1 --keepAlive true --match ~blurroundrect ~patch_grid ~desk_carsvg ~inc0.gif ~inc1.gif ~incInterlaced.gif ~inc0.jpg ~incGray.jpg ~inc0.wbmp ~inc1.wbmp ~inc0.webp ~inc1.webp ~inc0.ico ~inc1.ico ~inc0.png ~inc1.png ~inc2.png ~inc12.png ~inc13.png ~inc14.png ~inc0.webp ~inc1.webp; echo $? >/data/local/tmp/rc@@@",
+      "@@@STEP_LOG_LINE@nanobench.sh@set -x; /data/local/tmp/nanobench -i /sdcard/revenge_of_the_skiabot/resources --skps /sdcard/revenge_of_the_skiabot/skps --images /sdcard/revenge_of_the_skiabot/images/nanobench --svgs /sdcard/revenge_of_the_skiabot/svgs --pre_log --scales 1.0 1.1 --nogpu --purgeBetweenBenches --texttraces /sdcard/revenge_of_the_skiabot/text_blob_traces --config 8888 nonrendering --loops 1 --samples 1 --keepAlive true --match ~blurroundrect ~patch_grid ~desk_carsvg ~inc0.gif ~inc1.gif ~incInterlaced.gif ~inc0.jpg ~incGray.jpg ~inc0.wbmp ~inc1.wbmp ~inc0.webp ~inc1.webp ~inc0.ico ~inc1.ico ~inc0.png ~inc1.png ~inc2.png ~inc12.png ~inc13.png ~inc14.png ~inc0.webp ~inc1.webp; echo $? >/data/local/tmp/rc@@@",
       "@@@STEP_LOG_END@nanobench.sh@@@"
     ]
   },
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android_Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android_Vulkan.json
index 6e6d3b0..5efcffc 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android_Vulkan.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-P30-GPU-MaliG76-arm64-Release-All-Android_Vulkan.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android.json b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android.json
index 22ea427..709f315 100644
--- a/infra/bots/recipes/perf.expected/Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android.json
+++ b/infra/bots/recipes/perf.expected/Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Release-All.json b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Release-All.json
index 98b370f..530bc1d 100644
--- a/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Release-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-ASUSChromebookFlipC100-GPU-MaliT764-arm-Release-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -139,7 +143,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -279,7 +287,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -419,7 +431,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-AcerChromebook13_CB5_311-GPU-TegraK1-arm-Release-All.json b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-AcerChromebook13_CB5_311-GPU-TegraK1-arm-Release-All.json
index ee7747b..ccc9b70 100644
--- a/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-AcerChromebook13_CB5_311-GPU-TegraK1-arm-Release-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-ChromeOS-Clang-AcerChromebook13_CB5_311-GPU-TegraK1-arm-Release-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -139,7 +143,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -279,7 +287,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -419,7 +431,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Debug-All.json b/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Debug-All.json
index 2da595d..cf75e8d 100644
--- a/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Debug-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Debug-All.json
@@ -141,7 +141,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -332,6 +336,7 @@
       "~^path_text_clipped",
       "~shapes_rrect_inner_rrect_50_500x500",
       "~compositing_images",
+      "~bulkrect",
       "--loops",
       "1"
     ],
diff --git a/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json b/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
index e88635b..72c3c90 100644
--- a/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
@@ -141,7 +141,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -364,6 +368,7 @@
       "~^path_text_clipped",
       "~shapes_rrect_inner_rrect_50_500x500",
       "~compositing_images",
+      "~bulkrect",
       "--outResultsFile",
       "/cache/skia/perf/nanobench_abc123_1337000001.json",
       "--properties",
diff --git a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
index cd4ef4a..376875f3d 100644
--- a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
+++ b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All.json b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All.json
index 0ca2e1c..53871df 100644
--- a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-BonusConfigs.json b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-BonusConfigs.json
index b9edf82..1dc19f3 100644
--- a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-BonusConfigs.json
+++ b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-BonusConfigs.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug-All-Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug-All-Vulkan.json
index e718d60..aab6ba5 100644
--- a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug-All-Vulkan.json
+++ b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Debug-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All.json b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All.json
index 943dc3d..ab99aad 100644
--- a/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json
index dd4ff46..57f4c95 100644
--- a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json
+++ b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-CommandBuffer.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json
index edb1161..b3cfb50 100644
--- a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json
+++ b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Release-All-Metal.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json
index e8398fa..c79923b 100644
--- a/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json
+++ b/infra/bots/recipes/perf.expected/Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-CommandBuffer.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json
similarity index 91%
rename from infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json
rename to infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json
index e89dbe4..d4c6f42 100644
--- a/infra/bots/recipes/perf.expected/Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json b/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json
index a1b36fe..639b2ae 100644
--- a/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json b/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
index 8dd085d..9d49d99 100644
--- a/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
+++ b/infra/bots/recipes/perf.expected/Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
index ccf548c..e8bbf54 100644
--- a/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
+++ b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
@@ -88,7 +88,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -214,7 +218,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -340,7 +348,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
index 0552f9f..1b49b8b 100644
--- a/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
+++ b/infra/bots/recipes/perf.expected/Perf-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
@@ -88,7 +88,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -214,7 +218,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -340,7 +348,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.expected/trybot.json b/infra/bots/recipes/perf.expected/trybot.json
index b80794a..6d612f9 100644
--- a/infra/bots/recipes/perf.expected/trybot.json
+++ b/infra/bots/recipes/perf.expected/trybot.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf.py b/infra/bots/recipes/perf.py
index 7d3836f..74b9f1b 100644
--- a/infra/bots/recipes/perf.py
+++ b/infra/bots/recipes/perf.py
@@ -72,6 +72,10 @@
     if 'Nexus7' in bot:
       args.append('--purgeBetweenBenches')  # Debugging skia:8929
 
+    if 'Android' in bot:
+      assert api.flavor.device_dirs.texttraces_dir
+      args.extend(['--texttraces', api.flavor.device_dirs.texttraces_dir])
+
   elif api.vars.builder_cfg.get('cpu_or_gpu') == 'GPU':
     args.append('--nocpu')
 
@@ -314,6 +318,7 @@
       '~^path_text_clipped', # Bot times out; skia:7190
       '~shapes_rrect_inner_rrect_50_500x500', # skia:7551
       '~compositing_images',
+      '~bulkrect'
     ])
     if 'Debug' in api.vars.builder_name:
       args.extend(['--loops', '1'])
@@ -359,6 +364,8 @@
     try:
       if 'Chromecast' in api.vars.builder_name:
         api.flavor.install(resources=True, skps=True)
+      elif all(v in api.vars.builder_name for v in ['Android', 'CPU']):
+        api.flavor.install(skps=True, images=True, svgs=True, resources=True, texttraces=True)
       else:
         api.flavor.install(skps=True, images=True, svgs=True, resources=True)
       perf_steps(api)
@@ -390,8 +397,8 @@
    'Metal'),
   ('Perf-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Release-All-'
    'CommandBuffer'),
-  ('Perf-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-'
-    'Valgrind_SK_CPU_LIMIT_SSE41'),
+  ('Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All'
+   '-Valgrind_SK_CPU_LIMIT_SSE41'),
   'Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE',
   'Perf-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan',
   'Perf-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All',
diff --git a/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json b/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json
index 0af30e9..db7a4ce 100644
--- a/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json
+++ b/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json
@@ -1,169 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)\ndest = os.path.join(copy_dest, 'canvaskit.js')\nshutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/canvaskit/canvaskit/bin",
-      "[START_DIR]/build",
-      "canvaskit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'canvaskit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -189,15 +26,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/canvaskit/perf_canvaskit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/canvaskit/perf_canvaskit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.wasm",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.wasm [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.js",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.js [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1",
       "/SRC/skia/infra/canvaskit/perf_canvaskit.sh",
       "--builder",
@@ -222,7 +224,7 @@
       "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "Performance tests of canvaskit with Docker"
+    "name": "Performance tests of CanvasKit with Docker"
   },
   {
     "name": "$result"
diff --git a/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json b/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json
index 59fc071..62744f5 100644
--- a/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json
+++ b/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json
@@ -1,171 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)\ndest = os.path.join(copy_dest, 'canvaskit.js')\nshutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/canvaskit/canvaskit/bin",
-      "[START_DIR]/build",
-      "canvaskit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'canvaskit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -191,15 +26,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/canvaskit/perf_canvaskit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/canvaskit/perf_canvaskit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.wasm",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.wasm [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.js",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.js [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1",
       "/SRC/skia/infra/canvaskit/perf_canvaskit.sh",
       "--builder",
@@ -221,16 +221,14 @@
       "--issue",
       "1234",
       "--patchset",
-      "7",
-      "--patch_storage",
-      "gerrit"
+      "7"
     ],
     "env": {
       "CHROME_HEADLESS": "1",
       "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "Performance tests of canvaskit with Docker"
+    "name": "Performance tests of CanvasKit with Docker"
   },
   {
     "name": "$result"
diff --git a/infra/bots/recipes/perf_canvaskit.py b/infra/bots/recipes/perf_canvaskit.py
index 7d46109..dcb7bed 100644
--- a/infra/bots/recipes/perf_canvaskit.py
+++ b/infra/bots/recipes/perf_canvaskit.py
@@ -2,10 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Recipe which runs the PathKit tests using docker
+# Recipe which runs the Canvaskit tests using docker
 
 DEPS = [
   'checkout',
+  'docker',
   'env',
   'infra',
   'recipe_engine/file',
@@ -19,102 +20,53 @@
 
 
 DOCKER_IMAGE = 'gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1'
-INNER_KARMA_SCRIPT = '/SRC/skia/infra/canvaskit/perf_canvaskit.sh'
+INNER_KARMA_SCRIPT = 'skia/infra/canvaskit/perf_canvaskit.sh'
 
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
+  checkout_root = api.path['start_dir']
   out_dir = api.vars.swarming_out_dir
-  api.checkout.bot_update(checkout_root=checkout_root)
-
-  # Make sure this exists, otherwise Docker will make it with root permissions.
-  api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
 
   # The karma script is configured to look in ./canvaskit/bin/ for
   # the test files to load, so we must copy them there (see Set up for docker).
   copy_dest = checkout_root.join('skia', 'modules', 'canvaskit',
                                  'canvaskit', 'bin')
-
   base_dir = api.vars.build_dir
-  bundle_name = 'canvaskit.wasm'
+  copies = {
+    base_dir.join('canvaskit.js'):   copy_dest.join('canvaskit.js'),
+    base_dir.join('canvaskit.wasm'): copy_dest.join('canvaskit.wasm'),
+  }
+  recursive_read = [checkout_root.join('skia')]
 
-  api.python.inline(
-      name='Set up for docker',
-      program='''import errno
-import os
-import shutil
-import sys
-
-copy_dest = sys.argv[1]
-base_dir = sys.argv[2]
-bundle_name = sys.argv[3]
-out_dir = sys.argv[4]
-
-# Clean out old binaries (if any)
-try:
-  shutil.rmtree(copy_dest)
-except OSError as e:
-  if e.errno != errno.ENOENT:
-    raise
-
-# Make folder
-try:
-  os.makedirs(copy_dest)
-except OSError as e:
-  if e.errno != errno.EEXIST:
-    raise
-
-# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests
-# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)
-dest = os.path.join(copy_dest, 'canvaskit.js')
-shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)
-os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-if bundle_name:
-  dest = os.path.join(copy_dest, bundle_name)
-  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)
-  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-# Prepare output folder, api.file.ensure_directory doesn't touch
-# the permissions of the out directory if it already exists.
-os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.
-''',
-      args=[copy_dest, base_dir, bundle_name, out_dir],
-      infra_step=True)
-
-
-  cmd = ['docker', 'run', '--shm-size=2gb', '--rm',
-         '--volume', '%s:/SRC' % checkout_root,
-         '--volume', '%s:/OUT' % out_dir]
-
-  cmd.extend([
-    DOCKER_IMAGE,             INNER_KARMA_SCRIPT,
+  args = [
     '--builder',              api.vars.builder_name,
     '--git_hash',             api.properties['revision'],
-    '--buildbucket_build_id', api.properties.get('buildbucket_build_id',
-                                              ''),
+    '--buildbucket_build_id', api.properties.get('buildbucket_build_id', ''),
     '--bot_id',               api.vars.swarming_bot_id,
     '--task_id',              api.vars.swarming_task_id,
     '--browser',              'Chrome',
     '--config',               api.vars.configuration,
     '--source_type',          'canvaskit',
-    ])
-
+  ]
   if api.vars.is_trybot:
-    cmd.extend([
+    args.extend([
       '--issue',         api.vars.issue,
       '--patchset',      api.vars.patchset,
-      '--patch_storage', api.vars.patch_storage,
     ])
 
-  # Override DOCKER_CONFIG set by Kitchen.
-  env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
-  with api.env(env):
-    api.run(
-        api.step,
-        'Performance tests of canvaskit with Docker',
-        cmd=cmd)
+  api.docker.run(
+      name='Performance tests of CanvasKit with Docker',
+      docker_image=DOCKER_IMAGE,
+      src_dir=checkout_root,
+      out_dir=out_dir,
+      script=checkout_root.join(INNER_KARMA_SCRIPT),
+      args=args,
+      docker_args=None,
+      copies=copies,
+      recursive_read=recursive_read,
+      attempts=3,
+  )
 
 
 def GenTests(api):
diff --git a/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json b/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
index 7ead309..60fbd6f 100644
--- a/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
+++ b/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
@@ -9,100 +9,6 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
       "[START_DIR]/[SWARM_OUT_DIR]"
     ],
     "infra_step": true,
@@ -112,60 +18,6 @@
     "cmd": [
       "python",
       "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-asmjs/bin",
-      "[START_DIR]/build",
-      "pathkit.js.mem",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
     ],
     "name": "get swarming bot id",
@@ -189,15 +41,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/perf_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/perf_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-asmjs/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js.mem",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js.mem"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js.mem [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js.mem",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js.mem"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js.mem",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-asmjs/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "--env",
       "ASM_JS=1",
       "gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1",
diff --git a/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json b/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json
index bcb8e01..68894ae 100644
--- a/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json
+++ b/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json
@@ -9,100 +9,6 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
       "[START_DIR]/[SWARM_OUT_DIR]"
     ],
     "infra_step": true,
@@ -112,60 +18,6 @@
     "cmd": [
       "python",
       "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-wasm/bin",
-      "[START_DIR]/build",
-      "pathkit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
     ],
     "name": "get swarming bot id",
@@ -189,15 +41,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/perf_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/perf_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.wasm",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.wasm [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1",
       "/SRC/skia/infra/pathkit/perf_pathkit.sh",
       "--builder",
diff --git a/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json b/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json
index 423b1b4..a93a194 100644
--- a/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json
+++ b/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json
@@ -9,102 +9,6 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
       "[START_DIR]/[SWARM_OUT_DIR]"
     ],
     "infra_step": true,
@@ -114,60 +18,6 @@
     "cmd": [
       "python",
       "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-wasm/bin",
-      "[START_DIR]/build",
-      "pathkit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
     ],
     "name": "get swarming bot id",
@@ -191,15 +41,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/perf_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/perf_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.wasm",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.wasm [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1",
       "/SRC/skia/infra/pathkit/perf_pathkit.sh",
       "--builder",
@@ -221,9 +236,7 @@
       "--issue",
       "1234",
       "--patchset",
-      "7",
-      "--patch_storage",
-      "gerrit"
+      "7"
     ],
     "env": {
       "CHROME_HEADLESS": "1",
diff --git a/infra/bots/recipes/perf_pathkit.py b/infra/bots/recipes/perf_pathkit.py
index 64c5398..d520cc0 100644
--- a/infra/bots/recipes/perf_pathkit.py
+++ b/infra/bots/recipes/perf_pathkit.py
@@ -6,6 +6,7 @@
 
 DEPS = [
   'checkout',
+  'docker',
   'env',
   'infra',
   'recipe_engine/file',
@@ -19,14 +20,13 @@
 
 
 DOCKER_IMAGE = 'gcr.io/skia-public/perf-karma-chrome-tests:77.0.3865.120_v1'
-INNER_KARMA_SCRIPT = '/SRC/skia/infra/pathkit/perf_pathkit.sh'
+INNER_KARMA_SCRIPT = 'skia/infra/pathkit/perf_pathkit.sh'
 
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
+  checkout_root = api.path['start_dir']
   out_dir = api.vars.swarming_out_dir
-  api.checkout.bot_update(checkout_root=checkout_root)
 
   # Make sure this exists, otherwise Docker will make it with root permissions.
   api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
@@ -34,97 +34,56 @@
   # The karma script is configured to look in ./npm-(asmjs|wasm)/bin/ for
   # the test files to load, so we must copy them there (see Set up for docker).
   copy_dest = checkout_root.join('skia', 'modules', 'pathkit',
-                        'npm-wasm', 'bin')
+                                 'npm-wasm', 'bin')
   if 'asmjs' in api.vars.builder_name:
     copy_dest = checkout_root.join('skia', 'modules', 'pathkit',
-                        'npm-asmjs', 'bin')
+                                   'npm-asmjs', 'bin')
 
   base_dir = api.vars.build_dir
   bundle_name = 'pathkit.wasm'
   if 'asmjs' in api.vars.builder_name:
     bundle_name = 'pathkit.js.mem'
 
-  api.python.inline(
-      name='Set up for docker',
-      program='''import errno
-import os
-import shutil
-import sys
+  copies = {
+    base_dir.join('pathkit.js'): copy_dest.join('pathkit.js'),
+    base_dir.join(bundle_name):  copy_dest.join(bundle_name),
+  }
+  recursive_read = [checkout_root.join('skia')]
 
-copy_dest = sys.argv[1]
-base_dir = sys.argv[2]
-bundle_name = sys.argv[3]
-out_dir = sys.argv[4]
-
-# Clean out old binaries (if any)
-try:
-  shutil.rmtree(copy_dest)
-except OSError as e:
-  if e.errno != errno.ENOENT:
-    raise
-
-# Make folder
-try:
-  os.makedirs(copy_dest)
-except OSError as e:
-  if e.errno != errno.EEXIST:
-    raise
-
-# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests
-# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/)
-dest = os.path.join(copy_dest, 'pathkit.js')
-shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)
-os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-if bundle_name:
-  dest = os.path.join(copy_dest, bundle_name)
-  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)
-  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-# Prepare output folder, api.file.ensure_directory doesn't touch
-# the permissions of the out directory if it already exists.
-os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.
-''',
-      args=[copy_dest, base_dir, bundle_name, out_dir],
-      infra_step=True)
-
-  cmd = ['docker', 'run', '--shm-size=2gb', '--rm',
-         '--volume', '%s:/SRC' % checkout_root,
-         '--volume', '%s:/OUT' % out_dir]
-
+  docker_args = None
   if 'asmjs' in api.vars.builder_name:
-    cmd.extend(['--env', 'ASM_JS=1'])
+    docker_args = ['--env', 'ASM_JS=1']
 
-  cmd.extend([
-      DOCKER_IMAGE,             INNER_KARMA_SCRIPT,
-      '--builder',              api.vars.builder_name,
-      '--git_hash',             api.properties['revision'],
-      '--buildbucket_build_id', api.properties.get('buildbucket_build_id',
-                                                  ''),
-      '--bot_id',               api.vars.swarming_bot_id,
-      '--task_id',              api.vars.swarming_task_id,
-      '--browser',              'Chrome',
-      '--config',               api.vars.configuration,
-      '--source_type',          'pathkit',
-      ])
-
+  args = [
+    '--builder',              api.vars.builder_name,
+    '--git_hash',             api.properties['revision'],
+    '--buildbucket_build_id', api.properties.get('buildbucket_build_id', ''),
+    '--bot_id',               api.vars.swarming_bot_id,
+    '--task_id',              api.vars.swarming_task_id,
+    '--browser',              'Chrome',
+    '--config',               api.vars.configuration,
+    '--source_type',          'pathkit',
+  ]
   if 'asmjs' in api.vars.builder_name:
-    cmd.extend(['--compiled_language', 'asmjs']) # the default is wasm
-
+    args.extend(['--compiled_language', 'asmjs']) # the default is wasm
   if api.vars.is_trybot:
-    cmd.extend([
+    args.extend([
       '--issue',         api.vars.issue,
       '--patchset',      api.vars.patchset,
-      '--patch_storage', api.vars.patch_storage,
     ])
 
-  # Override DOCKER_CONFIG set by Kitchen.
-  env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
-  with api.env(env):
-    api.run(
-        api.step,
-        'Performance tests of PathKit with Docker',
-        cmd=cmd)
+  api.docker.run(
+      name='Performance tests of PathKit with Docker',
+      docker_image=DOCKER_IMAGE,
+      src_dir=checkout_root,
+      out_dir=out_dir,
+      script=checkout_root.join(INNER_KARMA_SCRIPT),
+      args=args,
+      docker_args=docker_args,
+      copies=copies,
+      recursive_read=recursive_read,
+      attempts=3,
+  )
 
 
 def GenTests(api):
diff --git a/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json b/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
index a5675f6..06bc7d8 100644
--- a/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
+++ b/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json b/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json
index 0f89ae8..b728f46 100644
--- a/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json
+++ b/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -139,7 +143,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/2.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/2.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@2.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -275,7 +282,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/3.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/3.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@3.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -411,7 +421,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/4.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/4.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@4.json@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json b/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json
index c5a7ba9..9d91742 100644
--- a/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json
+++ b/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -142,7 +146,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/2.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/2.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@2.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -281,7 +288,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/3.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/3.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@3.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -420,7 +430,10 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read [START_DIR]/[SWARM_OUT_DIR]/4.json"
+    "name": "read [START_DIR]/[SWARM_OUT_DIR]/4.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@4.json@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json b/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json
index 98d6685..b3d4f4e 100644
--- a/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json
+++ b/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json b/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json
index 65d49e5..7b0b4e1 100644
--- a/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json
+++ b/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf.json
index 8bca672..053a386 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf.json
@@ -6,100 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -118,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -129,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -137,7 +43,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -292,7 +198,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -300,7 +206,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -455,7 +361,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -463,7 +369,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf_trybot.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf_trybot.json
index 19545e9..14c656d 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf_trybot.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf_trybot.json
@@ -6,102 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -120,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -131,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -139,7 +43,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -294,7 +198,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -302,7 +206,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -457,7 +361,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "canvas",
       "--input",
@@ -465,7 +369,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
index 51c9b21..02fe698 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
@@ -6,100 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -118,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -129,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -137,7 +43,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -292,7 +198,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -300,7 +206,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -455,7 +361,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -463,7 +369,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
index e7737a8..40bf339 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
@@ -6,102 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -120,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -131,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -139,7 +43,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -294,7 +198,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -302,7 +206,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -457,7 +361,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/lottie-web-perf/lottie-web-perf.js",
+      "[START_DIR]/skia/tools/lottie-web-perf/lottie-web-perf.js",
       "--backend",
       "svg",
       "--input",
@@ -465,7 +369,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/lottie-web-perf",
+    "cwd": "[START_DIR]/skia/tools/lottie-web-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
index 72d1bf4..5eee3f0 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
@@ -6,100 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -118,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -129,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -139,7 +45,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -294,7 +200,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -304,7 +210,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -459,7 +365,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -469,7 +375,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_gpu.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_gpu.json
index 9bfd54b..78de176 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_gpu.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_gpu.json
@@ -6,100 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -118,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -129,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -140,7 +46,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -295,7 +201,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -306,7 +212,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -461,7 +367,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -472,7 +378,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
index 10b51b3..56c229f 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
@@ -6,102 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -120,7 +24,7 @@
       "npm",
       "install"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env_prefixes": {
       "PATH": [
         "[START_DIR]/node/node/bin"
@@ -131,7 +35,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -141,7 +45,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -296,7 +200,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -306,7 +210,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
@@ -461,7 +365,7 @@
   {
     "cmd": [
       "[START_DIR]/node/node/bin/node",
-      "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
+      "[START_DIR]/skia/tools/skottie-wasm-perf/skottie-wasm-perf.js",
       "--canvaskit_js",
       "[START_DIR]/build/canvaskit.js",
       "--canvaskit_wasm",
@@ -471,7 +375,7 @@
       "--output",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json"
     ],
-    "cwd": "[START_DIR]/cache/work/skia/tools/skottie-wasm-perf",
+    "cwd": "[START_DIR]/skia/tools/skottie-wasm-perf",
     "env": {
       "CHROME_HEADLESS": "1",
       "DISPLAY": ":0",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json
index 37583de..80b85ca 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json
@@ -6,100 +6,6 @@
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
       "--json-output",
       "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
       "listdir",
       "[START_DIR]/lottie-samples"
     ],
@@ -129,7 +35,7 @@
       "    arg_names, **additional_args)",
       "  File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
       "    return callable_obj(*props, **additional_args)",
-      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 129, in RunSteps",
+      "  File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 128, in RunSteps",
       "    raise Exception('Could not recognize the buildername %s' % buildername)",
       "Exception: Could not recognize the buildername Perf-Debian9-none-GCE-CPU-AVX2-x86_64-Release-All-Unrecognized"
     ]
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.py b/infra/bots/recipes/perf_skottiewasm_lottieweb.py
index d061a50..e67486e 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.py
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.py
@@ -83,8 +83,7 @@
 def RunSteps(api):
   api.vars.setup()
   api.flavor.setup()
-  checkout_root = api.checkout.default_checkout_root
-  api.checkout.bot_update(checkout_root=checkout_root)
+  checkout_root = api.path['start_dir']
   buildername = api.properties['buildername']
   node_path = api.path['start_dir'].join('node', 'node', 'bin', 'node')
   lottie_files = api.file.listdir(
diff --git a/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
index 26eff26..79f530e 100644
--- a/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
+++ b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Nightly-RecreateSKPs_Canary.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json
index ac43ed9..0176280 100644
--- a/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json
+++ b/infra/bots/recipes/recreate_skps.expected/Housekeeper-Weekly-RecreateSKPs.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/recreate_skps.expected/failed_upload.json b/infra/bots/recipes/recreate_skps.expected/failed_upload.json
index 4c81389..a503295 100644
--- a/infra/bots/recipes/recreate_skps.expected/failed_upload.json
+++ b/infra/bots/recipes/recreate_skps.expected/failed_upload.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench.json b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench.json
index c409049..834adef 100644
--- a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench.json
+++ b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_CCPR_Skpbench.json
@@ -56,7 +56,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
index 09ed82f..07e39e6 100644
--- a/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
+++ b/infra/bots/recipes/skpbench.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json
@@ -56,7 +56,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get mskp VERSION"
+    "name": "Get mskp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json
index 2b8c086..f15b836 100644
--- a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json
+++ b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLRecord_9x9.json b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLRecord_9x9.json
index 90fb820..6d36b87 100644
--- a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLRecord_9x9.json
+++ b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLRecord_9x9.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json
index 556f506..c7aa4e7 100644
--- a/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json
+++ b/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/skpbench.expected/trybot.json b/infra/bots/recipes/skpbench.expected/trybot.json
index 8f74c6f..01dc18a 100644
--- a/infra/bots/recipes/skpbench.expected/trybot.json
+++ b/infra/bots/recipes/skpbench.expected/trybot.json
@@ -56,7 +56,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-GCC-x86_64-Release-Flutter_Android.json b/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json
similarity index 89%
rename from infra/bots/recipes/sync_and_compile.expected/Build-Debian9-GCC-x86_64-Release-Flutter_Android.json
rename to infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json
index dfbd968..ce40af5 100644
--- a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-GCC-x86_64-Release-Flutter_Android.json
+++ b/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json b/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json
index 53774b4..a964d4c 100644
--- a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json
+++ b/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-x86_64-Release-ParentRevision.json b/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-x86_64-Release-ParentRevision.json
deleted file mode 100644
index 82e088b..0000000
--- a/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-x86_64-Release-ParentRevision.json
+++ /dev/null
@@ -1,207 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123^"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123^\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/cache/work/skia/infra/bots/assets/clang_linux/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get clang_linux VERSION"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "[START_DIR]/cache/work/skia/bin/fetch-gn"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "fetch-gn"
-  },
-  {
-    "cmd": [
-      "[START_DIR]/cache/work/skia/bin/gn",
-      "gen",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ParentRevision/Release",
-      "--args=cc=\"[START_DIR]/clang_linux/bin/clang\" cxx=\"[START_DIR]/clang_linux/bin/clang++\" extra_cflags=[\"-B[START_DIR]/clang_linux/bin\", \"-DDUMMY_clang_linux_version=42\"] extra_ldflags=[\"-B[START_DIR]/clang_linux/bin\", \"-fuse-ld=lld\"] is_debug=false target_cpu=\"x86_64\" werror=true"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "gn gen"
-  },
-  {
-    "cmd": [
-      "ninja",
-      "-C",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ParentRevision/Release"
-    ],
-    "cwd": "[START_DIR]/cache/work/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "ninja"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport glob\nimport os\nimport shutil\nimport sys\n\nsrc = sys.argv[1]\ndst = sys.argv[2]\nbuild_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']\n\ntry:\n  os.makedirs(dst)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\nfor pattern in build_products:\n  path = os.path.join(src, pattern)\n  for f in glob.glob(path):\n    dst_path = os.path.join(dst, os.path.relpath(f, src))\n    if not os.path.isdir(os.path.dirname(dst_path)):\n      os.makedirs(os.path.dirname(dst_path))\n    print 'Copying build product %s to %s' % (f, dst_path)\n    shutil.move(f, dst_path)\n",
-      "[START_DIR]/cache/work/skia/out/Build-Debian9-Clang-x86_64-Release-ParentRevision/Release",
-      "[START_DIR]/[SWARM_OUT_DIR]/ParentRevision"
-    ],
-    "infra_step": true,
-    "name": "copy build products",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import glob@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@src = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@dst = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@build_products = ['dm', 'dm.exe', 'dm.app', 'nanobench.app', 'get_images_from_skps', 'get_images_from_skps.exe', 'hello-opencl', 'hello-opencl.exe', 'nanobench', 'nanobench.exe', 'skpbench', 'skpbench.exe', '*.so', '*.dll', '*.dylib', 'skia_launcher', 'skottie_tool', 'lib/*.so', 'run_testlab', 'skqp-universal-debug.apk', 'whitelist_devices.json']@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(dst)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@for pattern in build_products:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  path = os.path.join(src, pattern)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  for f in glob.glob(path):@@@",
-      "@@@STEP_LOG_LINE@python.inline@    dst_path = os.path.join(dst, os.path.relpath(f, src))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if not os.path.isdir(os.path.dirname(dst_path)):@@@",
-      "@@@STEP_LOG_LINE@python.inline@      os.makedirs(os.path.dirname(dst_path))@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Copying build product %s to %s' % (f, dst_path)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    shutil.move(f, dst_path)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json b/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
index 3742c95..41a66c3 100644
--- a/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
+++ b/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json
@@ -1,6 +1,31 @@
 [
   {
     "cmd": [
+      "python",
+      "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
       "vpython",
       "-u",
       "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
diff --git a/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json b/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json
index 2306532..01d8b89 100644
--- a/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json
+++ b/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json
@@ -3,6 +3,31 @@
     "cmd": [
       "python",
       "-u",
+      "\nimport subprocess\nimport sys\n\nwhich = 'where' if sys.platform == 'win32' else 'which'\ngit = subprocess.check_output([which, 'git'])\nprint 'git was found at %s' % git\nif 'cipd_bin_packages' not in git:\n  print >> sys.stderr, 'Git must be obtained through CIPD.'\n  sys.exit(1)\n"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>;RECIPE_REPO[depot_tools]"
+    },
+    "name": "Assert that Git is from CIPD",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import subprocess@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@which = 'where' if sys.platform == 'win32' else 'which'@@@",
+      "@@@STEP_LOG_LINE@python.inline@git = subprocess.check_output([which, 'git'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'git was found at %s' % git@@@",
+      "@@@STEP_LOG_LINE@python.inline@if 'cipd_bin_packages' not in git:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  print >> sys.stderr, 'Git must be obtained through CIPD.'@@@",
+      "@@@STEP_LOG_LINE@python.inline@  sys.exit(1)@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
       "RECIPE_MODULE[depot_tools::git]\\resources\\git_setup.py",
       "--path",
       "[START_DIR]\\skia",
@@ -89,7 +114,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get clang_win VERSION"
+    "name": "Get clang_win VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/sync_and_compile.py b/infra/bots/recipes/sync_and_compile.py
index fb3d81e..03c942d 100644
--- a/infra/bots/recipes/sync_and_compile.py
+++ b/infra/bots/recipes/sync_and_compile.py
@@ -31,7 +31,6 @@
   checkout_chromium = False
   checkout_flutter = False
   flutter_android = False
-  parent_rev = False
 
   if 'NoDEPS' in api.properties['buildername']:
     bot_update = False
@@ -43,16 +42,13 @@
     checkout_flutter = True
     if 'Android' in api.vars.builder_name:
       flutter_android = True
-  if 'ParentRevision' in api.vars.builder_name:
-    parent_rev = True
 
   if bot_update:
     api.checkout.bot_update(
         checkout_root=checkout_root,
         checkout_chromium=checkout_chromium,
         checkout_flutter=checkout_flutter,
-        flutter_android=flutter_android,
-        parent_rev=parent_rev)
+        flutter_android=flutter_android)
   else:
     api.checkout.git(checkout_root=checkout_root)
 
@@ -68,8 +64,6 @@
 
     # TODO(borenet): Move this out of the try/finally.
     dst = api.vars.swarming_out_dir
-    if 'ParentRevision' in api.vars.builder_name:
-      dst = api.vars.swarming_out_dir.join('ParentRevision')
     api.build.copy_build_products(out_dir=out_dir, dst=dst)
     if 'SKQP' in api.vars.extra_tokens:
       wlist = checkout_root.join(
@@ -94,8 +88,7 @@
 
 TEST_BUILDERS = [
   'Build-Debian9-Clang-universal-devrel-Android_SKQP',
-  'Build-Debian9-Clang-x86_64-Release-ParentRevision',
-  'Build-Debian9-GCC-x86_64-Release-Flutter_Android',
+  'Build-Debian9-Clang-arm-Release-Flutter_Android',
   'Build-Mac-Clang-x86_64-Debug-CommandBuffer',
   'Build-Win10-Clang-x86_64-Release-NoDEPS',
 ]
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
index e6244f1..0588c38 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android.json
index 954f2a8..c7ef51a 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android_NoGPUThreads.json b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android_NoGPUThreads.json
index c6195a4..a6d3749 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android_NoGPUThreads.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS6-GPU-MaliT760-arm64-Debug-All-Android_NoGPUThreads.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android_Vulkan.json
index 7ba31b2..936d105 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android_Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Release-All-Android_Vulkan.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-MotoG4-CPU-Snapdragon617-arm-Release-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-MotoG4-CPU-Snapdragon617-arm-Release-All-Android.json
index 342dfc0..c52007b 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-MotoG4-CPU-Snapdragon617-arm-Release-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-MotoG4-CPU-Snapdragon617-arm-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-All-Android_CCPR.json b/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-All-Android_CCPR.json
index 7afc1fb..2457d24 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-All-Android_CCPR.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-NVIDIA_Shield-GPU-TegraX1-arm64-Debug-All-Android_CCPR.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android.json
index 533ac57..f5e4d92 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-CPU-Tegra3-arm-Release-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-CPU-Tegra3-arm-Release-All-Android.json
index 5b358ad..e8ca7e7 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-CPU-Tegra3-arm-Release-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Nexus7-CPU-Tegra3-arm-Release-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json
index a34f3ef..767023d 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json
@@ -229,7 +229,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -427,7 +431,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -625,7 +633,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-All-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-All-Android_Vulkan.json
index 1bcac91..24ea642 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-All-Android_Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm64-Debug-All-Android_Vulkan.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json
index b620390..cab9e4e 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3-GPU-Adreno630-arm64-Debug-All-Android_Vulkan.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3-GPU-Adreno630-arm64-Debug-All-Android_Vulkan.json
index 04caf49..8f71f91 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3-GPU-Adreno630-arm64-Debug-All-Android_Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3-GPU-Adreno630-arm64-Debug-All-Android_Vulkan.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android.json
index 2d2791c..46728eb 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android.json b/infra/bots/recipes/test.expected/Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android.json
index 918d097..d11d6cf 100644
--- a/infra/bots/recipes/test.expected/Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android.json
+++ b/infra/bots/recipes/test.expected/Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-AcerChromebookR13Convertible-GPU-PowerVRGX6250-arm-Debug-All.json b/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-AcerChromebookR13Convertible-GPU-PowerVRGX6250-arm-Debug-All.json
index 49266f9..704fe59 100644
--- a/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-AcerChromebookR13Convertible-GPU-PowerVRGX6250-arm-Debug-All.json
+++ b/infra/bots/recipes/test.expected/Test-ChromeOS-Clang-AcerChromebookR13Convertible-GPU-PowerVRGX6250-arm-Debug-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -139,7 +143,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -279,7 +287,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -419,7 +431,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json b/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
index a7828e8..00a1e69 100644
--- a/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
+++ b/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-CPU-Cortex_A7-arm-Release-All.json
@@ -141,7 +141,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json b/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
index 772d0bc..e6f7ad1 100644
--- a/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
+++ b/infra/bots/recipes/test.expected/Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All.json
@@ -141,7 +141,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json b/infra/bots/recipes/test.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
similarity index 72%
copy from infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
copy to infra/bots/recipes/test.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
index 4af4d1f..c273563 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
+++ b/infra/bots/recipes/test.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -213,27 +225,108 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
     "cmd": [
-      "python",
+      "vpython",
       "-u",
-      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py"
+    ],
+    "env": {
+      "CHROME_HEADLESS": "1",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+    },
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "docker",
+      "run",
+      "--shm-size=2gb",
+      "--rm",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
+      "gcr.io/skia-public/gcc-debian10@sha256:89a72df1e2fdea6f774a3fa4199bb9aaa4a0526a3ac1f233e604d689b694f95c",
+      "/SRC/../RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
+      "/SRC",
       "catchsegv",
-      "[START_DIR]/build/dm",
+      "/SRC/build/dm",
       "--resourcePath",
-      "[START_DIR]/skia/resources",
+      "/SRC/skia/resources",
       "--skps",
-      "[START_DIR]/skp",
+      "/SRC/skp",
       "--images",
-      "[START_DIR]/skimage/dm",
+      "/SRC/skimage/dm",
       "--colorImages",
-      "[START_DIR]/skimage/colorspace",
+      "/SRC/skimage/colorspace",
       "--nameByHash",
       "--properties",
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3",
+      "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -243,194 +336,55 @@
       "swarming_task_id",
       "123456",
       "--svgs",
-      "[START_DIR]/svg",
+      "/SRC/svg",
       "--key",
       "arch",
       "x86_64",
       "compiler",
-      "Clang",
+      "GCC",
       "configuration",
       "Debug",
       "cpu_or_gpu",
-      "GPU",
+      "CPU",
       "cpu_or_gpu_value",
-      "QuadroP400",
+      "AVX2",
       "extra_config",
-      "DDL3",
+      "Docker",
       "model",
-      "Golo",
+      "GCE",
       "os",
-      "Ubuntu17",
+      "Debian10",
       "style",
-      "DDL",
+      "default",
       "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
+      "/SRC/tmp/uninteresting_hashes.txt",
       "--writePath",
-      "[START_DIR]/[SWARM_OUT_DIR]",
+      "/OUT",
       "--dont_write",
       "pdf",
       "--randomProcessorTest",
-      "--nocpu",
-      "--skpViewportSize",
-      "2048",
-      "--gpuThreads",
-      "0",
+      "--nogpu",
       "--config",
-      "ddl-gl",
-      "ddl2-gl",
+      "8888",
       "--src",
+      "tests",
       "gm",
-      "skp",
-      "--blacklist",
-      "gl1010102",
       "image",
-      "_",
-      "_",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "dftext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "_",
-      "svg",
-      "_",
-      "svgparse_",
+      "colorImage",
+      "--blacklist",
       "_",
       "image",
       "gen_platf",
       "error",
-      "_",
-      "image",
-      "_",
-      "interlaced1.png",
-      "_",
-      "image",
-      "_",
-      "interlaced2.png",
-      "_",
-      "image",
-      "_",
-      "interlaced3.png",
-      "_",
-      "image",
-      "_",
-      ".arw",
-      "_",
-      "image",
-      "_",
-      ".cr2",
-      "_",
-      "image",
-      "_",
-      ".dng",
-      "_",
-      "image",
-      "_",
-      ".nef",
-      "_",
-      "image",
-      "_",
-      ".nrw",
-      "_",
-      "image",
-      "_",
-      ".orf",
-      "_",
-      "image",
-      "_",
-      ".raf",
-      "_",
-      "image",
-      "_",
-      ".rw2",
-      "_",
-      "image",
-      "_",
-      ".pef",
-      "_",
-      "image",
-      "_",
-      ".srw",
-      "_",
-      "image",
-      "_",
-      ".ARW",
-      "_",
-      "image",
-      "_",
-      ".CR2",
-      "_",
-      "image",
-      "_",
-      ".DNG",
-      "_",
-      "image",
-      "_",
-      ".NEF",
-      "_",
-      "image",
-      "_",
-      ".NRW",
-      "_",
-      "image",
-      "_",
-      ".ORF",
-      "_",
-      "image",
-      "_",
-      ".RAF",
-      "_",
-      "image",
-      "_",
-      ".RW2",
-      "_",
-      "image",
-      "_",
-      ".PEF",
-      "_",
-      "image",
-      "_",
-      ".SRW",
       "--nonativeFonts",
       "--verbose"
     ],
-    "cwd": "[START_DIR]/skia",
     "env": {
       "CHROME_HEADLESS": "1",
+      "DOCKER_CONFIG": "/home/chrome-bot/.docker",
       "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
     },
-    "name": "symbolized dm"
+    "name": "symbolized dm in Docker"
   },
   {
     "name": "$result"
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
index a09cb22..2716abd 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -196,7 +208,6 @@
       "g8",
       "565",
       "pic-8888",
-      "tiles_rt-8888",
       "serialize-8888",
       "f16",
       "srgb",
@@ -598,14 +609,6 @@
       "gm",
       "_",
       "blurrect_compare",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
       "--nonativeFonts",
       "--verbose"
     ],
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
index baf2285..ab0f2ca 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-BonusConfigs.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -277,7 +289,6 @@
       "g8",
       "565",
       "pic-8888",
-      "tiles_rt-8888",
       "serialize-8888",
       "f16",
       "srgb",
@@ -674,14 +685,6 @@
       "gm",
       "_",
       "blurrect_compare",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
       "--nonativeFonts",
       "--verbose"
     ],
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
index c0614ce..a100472 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -195,7 +207,6 @@
       "g8",
       "565",
       "pic-8888",
-      "tiles_rt-8888",
       "serialize-8888",
       "f16",
       "srgb",
@@ -592,14 +603,6 @@
       "gm",
       "_",
       "blurrect_compare",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
       "--match",
       "~Once",
       "~Shared",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
index fb75337..3149540 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_10-Coverage.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_10-Coverage.json
index bc94533..0594443 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_10-Coverage.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_10-Coverage.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
index a989ae8..29b69e9 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get lottie-samples VERSION"
+    "name": "Get lottie-samples VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER.json
index 9fa3664..85225c8 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
index 713cebb..f65e34a 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -196,7 +208,6 @@
       "g8",
       "565",
       "pic-8888",
-      "tiles_rt-8888",
       "serialize-8888",
       "f16",
       "srgb",
@@ -593,14 +604,6 @@
       "gm",
       "_",
       "blurrect_compare",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_bw",
-      "tiles_rt-8888",
-      "gm",
-      "_",
-      "complexclip4_aa",
       "--match",
       "~ReadWriteAlpha",
       "~RGBA4444TextureTest",
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader.json
index 62c6c5b..8fba753 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan.json
index 4eb806c..aaae0a3 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json b/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
index c14de95..930e53a 100644
--- a/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-CommandBuffer.json b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-CommandBuffer.json
index 51c1dd6..7993b9c 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-CommandBuffer.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All-CommandBuffer.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json
index f5fe7c5..3fbf28d 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All-NativeFonts.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json
index 8740841..35ddb92 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Release-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
index 52e7e08..a9bea12 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacBookPro11.5-GPU-RadeonHD8870M-x86_64-Debug-All-Metal.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json
index a41ca3b..97f50a7 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All-CommandBuffer.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json b/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json
index 81472eb..abff3d9 100644
--- a/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json
+++ b/infra/bots/recipes/test.expected/Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json b/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json
deleted file mode 100644
index a50d74d..0000000
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie.json
+++ /dev/null
@@ -1,373 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/lottie-samples/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get lottie-samples VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/LOTTIE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write LOTTIE_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@LOTTIE_VERSION@42@@@",
-      "@@@STEP_LOG_END@LOTTIE_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "rmtree",
-      "[START_DIR]/test"
-    ],
-    "infra_step": true,
-    "name": "rmtree test"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/test"
-    ],
-    "infra_step": true,
-    "name": "makedirs test"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = sys.argv[1]\nRETRIES = 5\nTIMEOUT = 60\nWAIT_BASE = 15\n\nsocket.setdefaulttimeout(TIMEOUT)\nfor retry in range(RETRIES):\n  try:\n    with contextlib.closing(\n        urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:\n      hashes = w.read()\n      with open(sys.argv[2], 'w') as f:\n        f.write(hashes)\n        break\n  except Exception as e:\n    print 'Failed to get uninteresting hashes from %s:' % HASHES_URL\n    print e\n    if retry == RETRIES:\n      raise\n    waittime = WAIT_BASE * math.pow(2, retry)\n    print 'Retry in %d seconds.' % waittime\n    time.sleep(waittime)\n",
-      "https://example.com/hashes.txt",
-      "[START_DIR]/tmp/uninteresting_hashes.txt"
-    ],
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "infra_step": true,
-    "name": "get uninteresting hashes",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@import contextlib@@@",
-      "@@@STEP_LOG_LINE@python.inline@import math@@@",
-      "@@@STEP_LOG_LINE@python.inline@import socket@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@import time@@@",
-      "@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@HASHES_URL = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@RETRIES = 5@@@",
-      "@@@STEP_LOG_LINE@python.inline@TIMEOUT = 60@@@",
-      "@@@STEP_LOG_LINE@python.inline@WAIT_BASE = 15@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@socket.setdefaulttimeout(TIMEOUT)@@@",
-      "@@@STEP_LOG_LINE@python.inline@for retry in range(RETRIES):@@@",
-      "@@@STEP_LOG_LINE@python.inline@  try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    with contextlib.closing(@@@",
-      "@@@STEP_LOG_LINE@python.inline@        urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      hashes = w.read()@@@",
-      "@@@STEP_LOG_LINE@python.inline@      with open(sys.argv[2], 'w') as f:@@@",
-      "@@@STEP_LOG_LINE@python.inline@        f.write(hashes)@@@",
-      "@@@STEP_LOG_LINE@python.inline@        break@@@",
-      "@@@STEP_LOG_LINE@python.inline@  except Exception as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Failed to get uninteresting hashes from %s:' % HASHES_URL@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print e@@@",
-      "@@@STEP_LOG_LINE@python.inline@    if retry == RETRIES:@@@",
-      "@@@STEP_LOG_LINE@python.inline@      raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@    waittime = WAIT_BASE * math.pow(2, retry)@@@",
-      "@@@STEP_LOG_LINE@python.inline@    print 'Retry in %d seconds.' % waittime@@@",
-      "@@@STEP_LOG_LINE@python.inline@    time.sleep(waittime)@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_BOT_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_TASK_ID', '')\n"
-    ],
-    "name": "get swarming task id",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_TASK_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/build/dm",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie",
-      "buildbucket_build_id",
-      "123454321",
-      "task_id",
-      "task_12345",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--lotties",
-      "[START_DIR]/skia/resources/skottie",
-      "[START_DIR]/lottie-samples",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "QuadroP400",
-      "extra_config",
-      "Lottie",
-      "model",
-      "Golo",
-      "os",
-      "Ubuntu17",
-      "renderer",
-      "skottie",
-      "style",
-      "default",
-      "--uninterestingHashesFile",
-      "[START_DIR]/tmp/uninteresting_hashes.txt",
-      "--writePath",
-      "[START_DIR]/[SWARM_OUT_DIR]",
-      "--dont_write",
-      "pdf",
-      "--randomProcessorTest",
-      "--nocpu",
-      "--config",
-      "gl",
-      "--src",
-      "lottie",
-      "--blacklist",
-      "gl1010102",
-      "image",
-      "_",
-      "_",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "dftext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "_",
-      "svg",
-      "_",
-      "svgparse_",
-      "_",
-      "image",
-      "gen_platf",
-      "error",
-      "_",
-      "image",
-      "_",
-      "interlaced1.png",
-      "_",
-      "image",
-      "_",
-      "interlaced2.png",
-      "_",
-      "image",
-      "_",
-      "interlaced3.png",
-      "_",
-      "image",
-      "_",
-      ".arw",
-      "_",
-      "image",
-      "_",
-      ".cr2",
-      "_",
-      "image",
-      "_",
-      ".dng",
-      "_",
-      "image",
-      "_",
-      ".nef",
-      "_",
-      "image",
-      "_",
-      ".nrw",
-      "_",
-      "image",
-      "_",
-      ".orf",
-      "_",
-      "image",
-      "_",
-      ".raf",
-      "_",
-      "image",
-      "_",
-      ".rw2",
-      "_",
-      "image",
-      "_",
-      ".pef",
-      "_",
-      "image",
-      "_",
-      ".srw",
-      "_",
-      "image",
-      "_",
-      ".ARW",
-      "_",
-      "image",
-      "_",
-      ".CR2",
-      "_",
-      "image",
-      "_",
-      ".DNG",
-      "_",
-      "image",
-      "_",
-      ".NEF",
-      "_",
-      "image",
-      "_",
-      ".NRW",
-      "_",
-      "image",
-      "_",
-      ".ORF",
-      "_",
-      "image",
-      "_",
-      ".RAF",
-      "_",
-      "image",
-      "_",
-      ".RW2",
-      "_",
-      "image",
-      "_",
-      ".PEF",
-      "_",
-      "image",
-      "_",
-      ".SRW",
-      "--nonativeFonts",
-      "--verbose"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage.json b/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage.json
deleted file mode 100644
index 224690b..0000000
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage.json
+++ /dev/null
@@ -1,324 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/skp/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get skp VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SKP_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SKP_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SKP_VERSION@42@@@",
-      "@@@STEP_LOG_END@SKP_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get skimage VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SK_IMAGE_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SK_IMAGE_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SK_IMAGE_VERSION@42@@@",
-      "@@@STEP_LOG_END@SK_IMAGE_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/skia/infra/bots/assets/svg/VERSION",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "Get svg VERSION"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "42",
-      "[START_DIR]/tmp/SVG_VERSION"
-    ],
-    "infra_step": true,
-    "name": "write SVG_VERSION",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@SVG_VERSION@42@@@",
-      "@@@STEP_LOG_END@SVG_VERSION@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
-    ],
-    "name": "get swarming bot id",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_BOT_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import os\nprint os.environ.get('SWARMING_TASK_ID', '')\n"
-    ],
-    "name": "get swarming task id",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_TASK_ID', '')@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
-      "[START_DIR]",
-      "catchsegv",
-      "[START_DIR]/build/dm",
-      "--resourcePath",
-      "[START_DIR]/skia/resources",
-      "--skps",
-      "[START_DIR]/skp",
-      "--images",
-      "[START_DIR]/skimage/dm",
-      "--colorImages",
-      "[START_DIR]/skimage/colorspace",
-      "--nameByHash",
-      "--properties",
-      "gitHash",
-      "abc123",
-      "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage",
-      "buildbucket_build_id",
-      "123454321",
-      "task_id",
-      "task_12345",
-      "swarming_bot_id",
-      "skia-bot-123",
-      "swarming_task_id",
-      "123456",
-      "--svgs",
-      "[START_DIR]/svg",
-      "--key",
-      "arch",
-      "x86_64",
-      "compiler",
-      "Clang",
-      "configuration",
-      "Debug",
-      "cpu_or_gpu",
-      "GPU",
-      "cpu_or_gpu_value",
-      "QuadroP400",
-      "extra_config",
-      "Vulkan_Coverage",
-      "model",
-      "Golo",
-      "os",
-      "Ubuntu17",
-      "style",
-      "default",
-      "--dont_write",
-      "pdf",
-      "--randomProcessorTest",
-      "--nocpu",
-      "--config",
-      "vk",
-      "vkmsaa8",
-      "vk1010102",
-      "--src",
-      "tests",
-      "gm",
-      "image",
-      "colorImage",
-      "svg",
-      "--blacklist",
-      "vk1010102",
-      "image",
-      "_",
-      "_",
-      "_",
-      "svg",
-      "_",
-      "svgparse_",
-      "_",
-      "image",
-      "gen_platf",
-      "error",
-      "_",
-      "image",
-      "_",
-      "interlaced1.png",
-      "_",
-      "image",
-      "_",
-      "interlaced2.png",
-      "_",
-      "image",
-      "_",
-      "interlaced3.png",
-      "_",
-      "image",
-      "_",
-      ".arw",
-      "_",
-      "image",
-      "_",
-      ".cr2",
-      "_",
-      "image",
-      "_",
-      ".dng",
-      "_",
-      "image",
-      "_",
-      ".nef",
-      "_",
-      "image",
-      "_",
-      ".nrw",
-      "_",
-      "image",
-      "_",
-      ".orf",
-      "_",
-      "image",
-      "_",
-      ".raf",
-      "_",
-      "image",
-      "_",
-      ".rw2",
-      "_",
-      "image",
-      "_",
-      ".pef",
-      "_",
-      "image",
-      "_",
-      ".srw",
-      "_",
-      "image",
-      "_",
-      ".ARW",
-      "_",
-      "image",
-      "_",
-      ".CR2",
-      "_",
-      "image",
-      "_",
-      ".DNG",
-      "_",
-      "image",
-      "_",
-      ".NEF",
-      "_",
-      "image",
-      "_",
-      ".NRW",
-      "_",
-      "image",
-      "_",
-      ".ORF",
-      "_",
-      "image",
-      "_",
-      ".RAF",
-      "_",
-      "image",
-      "_",
-      ".RW2",
-      "_",
-      "image",
-      "_",
-      ".PEF",
-      "_",
-      "image",
-      "_",
-      ".SRW",
-      "--nonativeFonts",
-      "--verbose"
-    ],
-    "cwd": "[START_DIR]/skia",
-    "env": {
-      "CHROME_HEADLESS": "1",
-      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib",
-      "LLVM_PROFILE_FILE": "[START_DIR]/[SWARM_OUT_DIR]/All.profraw",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/linux_vulkan_sdk/bin"
-    },
-    "name": "symbolized dm"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json
similarity index 95%
rename from infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json
rename to infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json
index 54c48f2..cd54544 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -233,7 +245,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1",
+      "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -260,7 +272,7 @@
       "model",
       "Golo",
       "os",
-      "Ubuntu17",
+      "Ubuntu18",
       "style",
       "DDL",
       "--uninterestingHashesFile",
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
similarity index 95%
rename from infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
rename to infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
index 4af4d1f..c301cf4 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -233,7 +245,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3",
+      "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -260,7 +272,7 @@
       "model",
       "Golo",
       "os",
-      "Ubuntu17",
+      "Ubuntu18",
       "style",
       "DDL",
       "--uninterestingHashesFile",
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan.json
similarity index 90%
copy from infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
copy to infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan.json
index 4af4d1f..e70d132 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3.json
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -233,7 +245,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3",
+      "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -256,13 +268,13 @@
       "cpu_or_gpu_value",
       "QuadroP400",
       "extra_config",
-      "DDL3",
+      "Vulkan",
       "model",
       "Golo",
       "os",
-      "Ubuntu17",
+      "Ubuntu18",
       "style",
-      "DDL",
+      "default",
       "--uninterestingHashesFile",
       "[START_DIR]/tmp/uninteresting_hashes.txt",
       "--writePath",
@@ -271,57 +283,21 @@
       "pdf",
       "--randomProcessorTest",
       "--nocpu",
-      "--skpViewportSize",
-      "2048",
-      "--gpuThreads",
-      "0",
       "--config",
-      "ddl-gl",
-      "ddl2-gl",
+      "vk",
+      "vkmsaa8",
+      "vk1010102",
       "--src",
+      "tests",
       "gm",
-      "skp",
+      "image",
+      "colorImage",
+      "svg",
       "--blacklist",
-      "gl1010102",
+      "vk1010102",
       "image",
       "_",
       "_",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestpersistentcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "dftext",
-      "gltestglslcache",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "atlastext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "dftext",
-      "gltestprecompile",
-      "gm",
-      "_",
-      "glyph_pos_h_b",
       "_",
       "svg",
       "_",
@@ -428,7 +404,8 @@
     "cwd": "[START_DIR]/skia",
     "env": {
       "CHROME_HEADLESS": "1",
-      "PATH": "<PATH>:RECIPE_REPO[depot_tools]"
+      "LD_LIBRARY_PATH": "[START_DIR]/linux_vulkan_sdk/lib",
+      "PATH": "<PATH>:RECIPE_REPO[depot_tools]:[START_DIR]/linux_vulkan_sdk/bin"
     },
     "name": "symbolized dm"
   },
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
similarity index 92%
rename from infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
rename to infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
index c09ec4a..4e1172e 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -159,7 +171,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41",
+      "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -174,7 +186,7 @@
       "arch",
       "x86_64",
       "compiler",
-      "GCC",
+      "Clang",
       "configuration",
       "Release",
       "cpu_or_gpu",
@@ -186,7 +198,7 @@
       "model",
       "Golo",
       "os",
-      "Ubuntu17",
+      "Ubuntu18",
       "style",
       "default",
       "--dont_write",
diff --git a/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json
similarity index 91%
rename from infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json
rename to infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json
index d9cbf73..ea023b0 100644
--- a/infra/bots/recipes/test.expected/Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json
+++ b/infra/bots/recipes/test.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -159,7 +171,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41",
+      "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -174,7 +186,7 @@
       "arch",
       "x86_64",
       "compiler",
-      "GCC",
+      "Clang",
       "configuration",
       "Release",
       "cpu_or_gpu",
@@ -186,7 +198,7 @@
       "model",
       "Golo",
       "os",
-      "Ubuntu17",
+      "Ubuntu18",
       "style",
       "default",
       "--dont_write",
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json
index c86e0b8..6073ae8 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json
index a95fad9..beae7af 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json
index 47249c6..0a4b0da 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ReleaseAndAbandonGpuContext.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts_GDI.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts_GDI.json
index 6947f44..15aa390 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts_GDI.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-CPU-AVX2-x86_64-Debug-All-NativeFonts_GDI.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-All-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-All-ANGLE.json
index 13c4742..b7b76bd 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-All-ANGLE.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUC5i7RYH-GPU-IntelIris6100-x86_64-Release-All-ANGLE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-All-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-All-ANGLE.json
index 624ff54..a1c160d 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-All-ANGLE.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-NUCD34010WYKH-GPU-IntelHD4400-x86_64-Release-All-ANGLE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
index 676f2bd..438b7bb 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-GTX660-x86_64-Release-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Vulkan.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Vulkan.json
index 35589fa..6f5263f 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Vulkan.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleA-GPU-RadeonHD7770-x86_64-Release-All-Vulkan.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json
index d55f153..e8721fc 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json b/infra/bots/recipes/test.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
index d663455..c0e643a 100644
--- a/infra/bots/recipes/test.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
+++ b/infra/bots/recipes/test.expected/Test-Win10-MSVC-LenovoYogaC630-GPU-Adreno630-arm64-Debug-All-ANGLE.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "read ssh_machine.json"
+    "name": "read ssh_machine.json",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@ssh_machine.json@{\"user_ip\": \"foo@127.0.0.1\"}@@@",
+      "@@@STEP_LOG_END@ssh_machine.json@@@"
+    ]
   },
   {
     "cmd": [
@@ -179,7 +183,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -233,7 +241,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SKP_VERSION.read SKP_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SKP_VERSION@@@"
     ]
   },
   {
@@ -352,7 +361,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -406,7 +419,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SK_IMAGE_VERSION.read SK_IMAGE_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SK_IMAGE_VERSION@@@"
     ]
   },
   {
@@ -525,7 +539,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -579,7 +597,8 @@
     "infra_step": true,
     "name": "read C:\\Users\\chrome-bot\\botdata\\SVG_VERSION.read SVG_VERSION",
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@SVG_VERSION@@@"
     ]
   },
   {
diff --git a/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FAAA.json b/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FAAA.json
index bda0989..87ec607 100644
--- a/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FAAA.json
+++ b/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FAAA.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FSAA.json b/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FSAA.json
index 82ac496..098ba8d 100644
--- a/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FSAA.json
+++ b/infra/bots/recipes/test.expected/Test-Win2016-Clang-GCE-CPU-AVX2-x86_64-Debug-All-FSAA.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
index d46fc09..e601c6e 100644
--- a/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
+++ b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPadPro-GPU-PowerVRGT7800-arm64-Release-All.json
@@ -88,7 +88,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -214,7 +218,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -340,7 +348,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/Test-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
index 0cfd6f7..d8efd18 100644
--- a/infra/bots/recipes/test.expected/Test-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
+++ b/infra/bots/recipes/test.expected/Test-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal.json
@@ -88,7 +88,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -214,7 +218,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -340,7 +348,11 @@
       "IOS_MOUNT_POINT": "[START_DIR]/mnt_iosdevice"
     },
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/failed_dm.json b/infra/bots/recipes/test.expected/failed_dm.json
index 32a9ff9..0cc34e6 100644
--- a/infra/bots/recipes/test.expected/failed_dm.json
+++ b/infra/bots/recipes/test.expected/failed_dm.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -233,7 +245,7 @@
       "gitHash",
       "abc123",
       "builder",
-      "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All",
+      "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All",
       "buildbucket_build_id",
       "123454321",
       "task_id",
@@ -248,7 +260,7 @@
       "arch",
       "x86_64",
       "compiler",
-      "GCC",
+      "Clang",
       "configuration",
       "Debug",
       "cpu_or_gpu",
diff --git a/infra/bots/recipes/test.expected/failed_get_hashes.json b/infra/bots/recipes/test.expected/failed_get_hashes.json
index a202e03..d170cfc 100644
--- a/infra/bots/recipes/test.expected/failed_get_hashes.json
+++ b/infra/bots/recipes/test.expected/failed_get_hashes.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/failed_pull.json b/infra/bots/recipes/test.expected/failed_pull.json
index 6ff1602..8ad4527 100644
--- a/infra/bots/recipes/test.expected/failed_pull.json
+++ b/infra/bots/recipes/test.expected/failed_pull.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/internal_bot_5.json b/infra/bots/recipes/test.expected/internal_bot_5.json
index 0194194..89da799 100644
--- a/infra/bots/recipes/test.expected/internal_bot_5.json
+++ b/infra/bots/recipes/test.expected/internal_bot_5.json
@@ -139,7 +139,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -337,7 +341,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -535,7 +543,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.expected/trybot.json b/infra/bots/recipes/test.expected/trybot.json
index 181c057..5225fb1 100644
--- a/infra/bots/recipes/test.expected/trybot.json
+++ b/infra/bots/recipes/test.expected/trybot.json
@@ -26,7 +26,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skp VERSION"
+    "name": "Get skp VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -58,7 +62,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get skimage VERSION"
+    "name": "Get skimage VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
@@ -90,7 +98,11 @@
       "/path/to/tmp/"
     ],
     "infra_step": true,
-    "name": "Get svg VERSION"
+    "name": "Get svg VERSION",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@VERSION@42@@@",
+      "@@@STEP_LOG_END@VERSION@@@"
+    ]
   },
   {
     "cmd": [
diff --git a/infra/bots/recipes/test.py b/infra/bots/recipes/test.py
index 6f4fa20..a022c1b 100644
--- a/infra/bots/recipes/test.py
+++ b/infra/bots/recipes/test.py
@@ -110,7 +110,7 @@
       configs.extend([
         'pdf',
         'g8', '565',
-        'pic-8888', 'tiles_rt-8888', 'serialize-8888',
+        'pic-8888', 'serialize-8888',
         'f16', 'srgb', 'esrgb', 'narrow', 'enarrow',
         'p3', 'ep3', 'rec2020', 'erec2020'])
 
@@ -326,9 +326,6 @@
       args.extend(['--skpViewportSize', "2048"])
       args.extend(['--gpuThreads', "0"])
 
-    if 'Lottie' in bot:
-      configs = ['gl']
-
   tf = api.vars.builder_cfg.get('test_filter')
   if 'All' != tf:
     # Expected format: shard_XX_YY
@@ -486,16 +483,6 @@
     # Android and iOS. skia:5438
     blacklist('_ test _ GrShape')
 
-  if api.vars.internal_hardware_label == '2':
-    # skia:7160
-    blacklist('_ test _ SRGBReadWritePixels')
-    blacklist('_ test _ SRGBMipMap')
-    # skia:9517
-    blacklist('_ test _ CharacterizationBackendAllocationTest')
-    blacklist('_ test _ ColorTypeBackendAllocationTest')
-    blacklist('_ test _ GLBackendAllocationTest')
-    blacklist('_ test _ VKBackendAllocationTest')
-
   if api.vars.internal_hardware_label == '5':
     # http://b/118312149#comment9
     blacklist('_ test _ SRGBReadWritePixels')
@@ -602,10 +589,6 @@
     blacklist([      'pic-8888', 'gm', '_', "blurrect_compare"])
     blacklist(['serialize-8888', 'gm', '_', "blurrect_compare"])
 
-  # GM that not support tiles_rt
-  for test in ['complexclip4_bw', 'complexclip4_aa']:
-    blacklist([ 'tiles_rt-8888', 'gm', '_', test])
-
   # Extensions for RAW images
   r = ['arw', 'cr2', 'dng', 'nef', 'nrw', 'orf', 'raf', 'rw2', 'pef', 'srw',
        'ARW', 'CR2', 'DNG', 'NEF', 'NRW', 'ORF', 'RAF', 'RW2', 'PEF', 'SRW']
@@ -1040,6 +1023,7 @@
   'Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Release-All-SwiftShader',
   'Test-Debian9-Clang-NUC5PPYH-GPU-IntelHD405-x86_64-Release-All-Vulkan',
   'Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan',
+  'Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker',
   'Test-iOS-Clang-iPhone6-GPU-PowerVRGX6450-arm64-Release-All-Metal',
   ('Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Release-All'
    '-NativeFonts'),
@@ -1048,14 +1032,13 @@
   ('Test-Mac10.13-Clang-MacMini7.1-GPU-IntelIris5100-x86_64-Debug-All'
    '-CommandBuffer'),
   'Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All',
-  'Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan_Coverage',
-  ('Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All'
+  'Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Vulkan',
+  ('Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All'
    '-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41'),
-  ('Test-Ubuntu17-GCC-Golo-GPU-QuadroP400-x86_64-Release-All'
+  ('Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All'
    '-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41'),
-  'Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1',
-  'Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3',
-  'Test-Ubuntu17-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-Lottie',
+  'Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL1',
+  'Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-DDL3',
   'Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-BonusConfigs',
   'Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All-NonNVPR',
   ('Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All'
@@ -1140,7 +1123,7 @@
     )
   )
 
-  builder = 'Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All'
+  builder = 'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All'
   yield (
     api.test('failed_dm') +
     api.properties(buildername=builder,
@@ -1243,28 +1226,6 @@
   )
 
   yield (
-    api.test('internal_bot_2') +
-    api.properties(buildername=builder,
-                   buildbucket_build_id='123454321',
-                   revision='abc123',
-                   path_config='kitchen',
-                   swarm_out_dir='[SWARM_OUT_DIR]',
-                   gold_hashes_url='https://example.com/hashes.txt',
-                   internal_hardware_label='2',
-                   task_id='task_12345') +
-    api.path.exists(
-        api.path['start_dir'].join('skia'),
-        api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                     'skimage', 'VERSION'),
-        api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                     'skp', 'VERSION'),
-        api.path['start_dir'].join('skia', 'infra', 'bots', 'assets',
-                                     'svg', 'VERSION'),
-        api.path['start_dir'].join('tmp', 'uninteresting_hashes.txt')
-    )
-  )
-
-  yield (
     api.test('internal_bot_5') +
     api.properties(buildername=builder,
                    buildbucket_build_id='123454321',
diff --git a/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json b/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json
index 864f442..e382257 100644
--- a/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json
+++ b/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json
@@ -9,158 +9,10 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
     ],
     "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)\ndest = os.path.join(copy_dest, 'canvaskit.js')\nshutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/canvaskit/canvaskit/bin",
-      "[START_DIR]/build",
-      "canvaskit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'canvaskit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
+    "name": "mkdirs copy_dest"
   },
   {
     "cmd": [
@@ -189,15 +41,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/canvaskit/test_canvaskit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/canvaskit/test_canvaskit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.wasm",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.wasm [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.js",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.js [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
       "/SRC/skia/infra/canvaskit/test_canvaskit.sh",
       "--builder",
diff --git a/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json b/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json
index dec669b..3b0202b 100644
--- a/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json
+++ b/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json
@@ -9,160 +9,10 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
     ],
     "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)\ndest = os.path.join(copy_dest, 'canvaskit.js')\nshutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/canvaskit/canvaskit/bin",
-      "[START_DIR]/build",
-      "canvaskit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'canvaskit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
+    "name": "mkdirs copy_dest"
   },
   {
     "cmd": [
@@ -191,15 +41,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/canvaskit/test_canvaskit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/canvaskit/test_canvaskit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.wasm",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.wasm [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/canvaskit/canvaskit/bin (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/canvaskit.js",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/canvaskit.js [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/canvaskit/canvaskit/bin/canvaskit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
       "/SRC/skia/infra/canvaskit/test_canvaskit.sh",
       "--builder",
diff --git a/infra/bots/recipes/test_canvaskit.py b/infra/bots/recipes/test_canvaskit.py
index d2a18bc..956e896 100644
--- a/infra/bots/recipes/test_canvaskit.py
+++ b/infra/bots/recipes/test_canvaskit.py
@@ -6,6 +6,7 @@
 
 DEPS = [
   'checkout',
+  'docker',
   'env',
   'infra',
   'recipe_engine/file',
@@ -19,101 +20,54 @@
 
 
 DOCKER_IMAGE = 'gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2'
-INNER_KARMA_SCRIPT = '/SRC/skia/infra/canvaskit/test_canvaskit.sh'
+INNER_KARMA_SCRIPT = 'skia/infra/canvaskit/test_canvaskit.sh'
 
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
+  checkout_root = api.path['start_dir']
   out_dir = api.vars.swarming_out_dir
-  api.checkout.bot_update(checkout_root=checkout_root)
-
-  # Make sure this exists, otherwise Docker will make it with root permissions.
-  api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
 
   # The karma script is configured to look in ./canvaskit/bin/ for
   # the test files to load, so we must copy them there (see Set up for docker).
   copy_dest = checkout_root.join('skia', 'modules', 'canvaskit',
                                  'canvaskit', 'bin')
-
+  api.file.ensure_directory('mkdirs copy_dest', copy_dest, mode=0777)
   base_dir = api.vars.build_dir
-  bundle_name = 'canvaskit.wasm'
+  copies = {
+    base_dir.join('canvaskit.js'): copy_dest.join('canvaskit.js'),
+    base_dir.join('canvaskit.wasm'):    copy_dest.join('canvaskit.wasm'),
+  }
+  recursive_read = [checkout_root.join('skia')]
 
-  api.python.inline(
-      name='Set up for docker',
-      program='''import errno
-import os
-import shutil
-import sys
-
-copy_dest = sys.argv[1]
-base_dir = sys.argv[2]
-bundle_name = sys.argv[3]
-out_dir = sys.argv[4]
-
-# Clean out old binaries (if any)
-try:
-  shutil.rmtree(copy_dest)
-except OSError as e:
-  if e.errno != errno.ENOENT:
-    raise
-
-# Make folder
-try:
-  os.makedirs(copy_dest)
-except OSError as e:
-  if e.errno != errno.EEXIST:
-    raise
-
-# Copy binaries (canvaskit.js and canvaskit.wasm) to where the karma tests
-# expect them ($SKIA_ROOT/modules/canvaskit/canvaskit/bin/)
-dest = os.path.join(copy_dest, 'canvaskit.js')
-shutil.copyfile(os.path.join(base_dir, 'canvaskit.js'), dest)
-os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-if bundle_name:
-  dest = os.path.join(copy_dest, bundle_name)
-  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)
-  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-# Prepare output folder, api.file.ensure_directory doesn't touch
-# the permissions of the out directory if it already exists.
-os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.
-''',
-      args=[copy_dest, base_dir, bundle_name, out_dir],
-      infra_step=True)
-
-
-  cmd = ['docker', 'run', '--shm-size=2gb', '--rm',
-         '--volume', '%s:/SRC' % checkout_root,
-         '--volume', '%s:/OUT' % out_dir]
-
-  cmd.extend([
-      DOCKER_IMAGE,             INNER_KARMA_SCRIPT,
-      '--builder',              api.vars.builder_name,
-      '--git_hash',             api.properties['revision'],
-      '--buildbucket_build_id', api.properties.get('buildbucket_build_id',
-                                                  ''),
-      '--bot_id',               api.vars.swarming_bot_id,
-      '--task_id',              api.vars.swarming_task_id,
-      '--browser',              'Chrome',
-      '--config',               api.vars.configuration,
-      '--source_type',          'canvaskit',
-      ])
-
+  args = [
+    '--builder',              api.vars.builder_name,
+    '--git_hash',             api.properties['revision'],
+    '--buildbucket_build_id', api.properties.get('buildbucket_build_id', ''),
+    '--bot_id',               api.vars.swarming_bot_id,
+    '--task_id',              api.vars.swarming_task_id,
+    '--browser',              'Chrome',
+    '--config',               api.vars.configuration,
+    '--source_type',          'canvaskit',
+  ]
   if api.vars.is_trybot:
-    cmd.extend([
+    args.extend([
       '--issue',         api.vars.issue,
       '--patchset',      api.vars.patchset,
     ])
 
-  # Override DOCKER_CONFIG set by Kitchen.
-  env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
-  with api.env(env):
-    api.run(
-        api.step,
-        'Test CanvasKit with Docker',
-        cmd=cmd)
+  api.docker.run(
+      name='Test CanvasKit with Docker',
+      docker_image=DOCKER_IMAGE,
+      src_dir=checkout_root,
+      out_dir=out_dir,
+      script=checkout_root.join(INNER_KARMA_SCRIPT),
+      args=args,
+      docker_args=None,
+      copies=copies,
+      recursive_read=recursive_read,
+      attempts=3,
+  )
 
 
 def GenTests(api):
diff --git a/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json b/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json
index 0453b1f..6aaa0fb 100644
--- a/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json
+++ b/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json
@@ -9,100 +9,6 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
       "[START_DIR]/[SWARM_OUT_DIR]"
     ],
     "infra_step": true,
@@ -142,7 +48,7 @@
       "\nimport os\nimport sys\n\nlottie_files_dir = sys.argv[1]\nout_dir = sys.argv[2]\nlottie_build = sys.argv[3]\n\n# Make sure all the lottie files are readable by everyone so we can see\n# them in the docker container.\nos.system('chmod 0644 %s/*' % lottie_files_dir)\nos.system('chmod 0644 %s/*' % lottie_build)\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\n# This typically means that the non-privileged docker won't be able to write.\nos.chmod(out_dir, 0o777)\n",
       "/tmp/lottie_files",
       "[START_DIR]/[SWARM_OUT_DIR]",
-      "[START_DIR]/cache/work/lottie/build/player"
+      "[START_DIR]/lottie/build/player"
     ],
     "infra_step": true,
     "name": "Set up for docker",
@@ -200,11 +106,11 @@
       "--shm-size=2gb",
       "--rm",
       "-v",
-      "[START_DIR]/cache/work:/SRC",
+      "[START_DIR]:/SRC",
       "-v",
       "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
       "-v",
-      "[START_DIR]/cache/work/lottie/build/player:/LOTTIE_BUILD",
+      "[START_DIR]/lottie/build/player:/LOTTIE_BUILD",
       "-v",
       "/tmp/lottie_files:/LOTTIE_FILES",
       "gcr.io/skia-public/gold-lottie-web-puppeteer:v2",
diff --git a/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json b/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json
index 8ad9f4e..4701c92 100644
--- a/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json
+++ b/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json
@@ -9,102 +9,6 @@
       "ensure-directory",
       "--mode",
       "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
       "[START_DIR]/[SWARM_OUT_DIR]"
     ],
     "infra_step": true,
@@ -144,7 +48,7 @@
       "\nimport os\nimport sys\n\nlottie_files_dir = sys.argv[1]\nout_dir = sys.argv[2]\nlottie_build = sys.argv[3]\n\n# Make sure all the lottie files are readable by everyone so we can see\n# them in the docker container.\nos.system('chmod 0644 %s/*' % lottie_files_dir)\nos.system('chmod 0644 %s/*' % lottie_build)\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\n# This typically means that the non-privileged docker won't be able to write.\nos.chmod(out_dir, 0o777)\n",
       "/tmp/lottie_files",
       "[START_DIR]/[SWARM_OUT_DIR]",
-      "[START_DIR]/cache/work/lottie/build/player"
+      "[START_DIR]/lottie/build/player"
     ],
     "infra_step": true,
     "name": "Set up for docker",
@@ -202,11 +106,11 @@
       "--shm-size=2gb",
       "--rm",
       "-v",
-      "[START_DIR]/cache/work:/SRC",
+      "[START_DIR]:/SRC",
       "-v",
       "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
       "-v",
-      "[START_DIR]/cache/work/lottie/build/player:/LOTTIE_BUILD",
+      "[START_DIR]/lottie/build/player:/LOTTIE_BUILD",
       "-v",
       "/tmp/lottie_files:/LOTTIE_FILES",
       "gcr.io/skia-public/gold-lottie-web-puppeteer:v2",
diff --git a/infra/bots/recipes/test_lottie_web.py b/infra/bots/recipes/test_lottie_web.py
index 39df24b..a406a98 100644
--- a/infra/bots/recipes/test_lottie_web.py
+++ b/infra/bots/recipes/test_lottie_web.py
@@ -24,7 +24,7 @@
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
+  checkout_root = api.path['start_dir']
   out_dir = api.vars.swarming_out_dir
   lottie_files_src = api.vars.slave_dir.join('lottie-samples')
   lottie_files_dir = '/tmp/lottie_files'
@@ -33,8 +33,6 @@
   # the docker image.
   lottie_build = checkout_root.join('lottie', 'build', 'player')
 
-  api.checkout.bot_update(checkout_root=checkout_root)
-
   # Make sure this exists, otherwise Docker will make it with root permissions.
   api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
   # When lottie files are brought in via isolate or CIPD, they are just
diff --git a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json
index 84898c7..d8e592c 100644
--- a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json
+++ b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json
@@ -1,169 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-asmjs/bin/test",
-      "[START_DIR]/build",
-      "",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -189,15 +26,133 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/test_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/test_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "--env",
       "ASM_JS=1",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
diff --git a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
index 9afede5..8bde211 100644
--- a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
+++ b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json
@@ -1,169 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-asmjs/bin/test",
-      "[START_DIR]/build",
-      "pathkit.js.mem",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -189,15 +26,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/test_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/test_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js.mem",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js.mem"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js.mem [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js.mem",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js.mem"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js.mem",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-asmjs/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "--env",
       "ASM_JS=1",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
diff --git a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json
index a3a54c2..7ecd73d 100644
--- a/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json
+++ b/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json
@@ -1,169 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-wasm/bin/test",
-      "[START_DIR]/build",
-      "pathkit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -189,15 +26,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/test_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/test_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.wasm",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.wasm [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
       "/SRC/skia/infra/pathkit/test_pathkit.sh",
       "--builder",
diff --git a/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json b/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json
index 392c04b..4cc3c0e 100644
--- a/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json
+++ b/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json
@@ -1,171 +1,6 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--patch_ref",
-      "https://skia.googlesource.com/skia.git@abc123:89/456789/12",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "mkdirs out_dir"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "import errno\nimport os\nimport shutil\nimport sys\n\ncopy_dest = sys.argv[1]\nbase_dir = sys.argv[2]\nbundle_name = sys.argv[3]\nout_dir = sys.argv[4]\n\n# Clean out old binaries (if any)\ntry:\n  shutil.rmtree(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.ENOENT:\n    raise\n\n# Make folder\ntry:\n  os.makedirs(copy_dest)\nexcept OSError as e:\n  if e.errno != errno.EEXIST:\n    raise\n\n# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests\n# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)\ndest = os.path.join(copy_dest, 'pathkit.js')\nshutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)\nos.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\nif bundle_name:\n  dest = os.path.join(copy_dest, bundle_name)\n  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)\n  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.\n\n# Prepare output folder, api.file.ensure_directory doesn't touch\n# the permissions of the out directory if it already exists.\nos.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.\n",
-      "[START_DIR]/cache/work/skia/modules/pathkit/npm-wasm/bin/test",
-      "[START_DIR]/build",
-      "pathkit.wasm",
-      "[START_DIR]/[SWARM_OUT_DIR]"
-    ],
-    "infra_step": true,
-    "name": "Set up for docker",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@python.inline@import errno@@@",
-      "@@@STEP_LOG_LINE@python.inline@import os@@@",
-      "@@@STEP_LOG_LINE@python.inline@import shutil@@@",
-      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@copy_dest = sys.argv[1]@@@",
-      "@@@STEP_LOG_LINE@python.inline@base_dir = sys.argv[2]@@@",
-      "@@@STEP_LOG_LINE@python.inline@bundle_name = sys.argv[3]@@@",
-      "@@@STEP_LOG_LINE@python.inline@out_dir = sys.argv[4]@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Clean out old binaries (if any)@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.rmtree(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.ENOENT:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Make folder@@@",
-      "@@@STEP_LOG_LINE@python.inline@try:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.makedirs(copy_dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@except OSError as e:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  if e.errno != errno.EEXIST:@@@",
-      "@@@STEP_LOG_LINE@python.inline@    raise@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests@@@",
-      "@@@STEP_LOG_LINE@python.inline@# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)@@@",
-      "@@@STEP_LOG_LINE@python.inline@dest = os.path.join(copy_dest, 'pathkit.js')@@@",
-      "@@@STEP_LOG_LINE@python.inline@shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@if bundle_name:@@@",
-      "@@@STEP_LOG_LINE@python.inline@  dest = os.path.join(copy_dest, bundle_name)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)@@@",
-      "@@@STEP_LOG_LINE@python.inline@  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.@@@",
-      "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@# Prepare output folder, api.file.ensure_directory doesn't touch@@@",
-      "@@@STEP_LOG_LINE@python.inline@# the permissions of the out directory if it already exists.@@@",
-      "@@@STEP_LOG_LINE@python.inline@os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.@@@",
-      "@@@STEP_LOG_END@python.inline@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
@@ -191,15 +26,180 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "Docker setup"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs out_dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "777",
+      "[START_DIR]/[SWARM_OUT_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 777 [START_DIR]/[SWARM_OUT_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "755",
+      "[START_DIR]"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 755 [START_DIR]",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "0755",
+      "[START_DIR]/skia/infra/pathkit/test_pathkit.sh"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 0755 [START_DIR]/skia/infra/pathkit/test_pathkit.sh",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.wasm",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.wasm [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.wasm",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.mkdirs [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/build/pathkit.js",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.cp [START_DIR]/build/pathkit.js [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "644",
+      "[START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod 644 [START_DIR]/skia/modules/pathkit/npm-wasm/bin/test/pathkit.js",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "chmod",
+      "-R",
+      "a+r",
+      "[START_DIR]/skia"
+    ],
+    "infra_step": true,
+    "name": "Docker setup.chmod -R a+r [START_DIR]/skia",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [
       "docker",
       "run",
       "--shm-size=2gb",
       "--rm",
-      "--volume",
-      "[START_DIR]/cache/work:/SRC",
-      "--volume",
-      "[START_DIR]/[SWARM_OUT_DIR]:/OUT",
+      "--mount",
+      "type=bind,source=[START_DIR],target=/SRC",
+      "--mount",
+      "type=bind,source=[START_DIR]/[SWARM_OUT_DIR],target=/OUT",
       "gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2",
       "/SRC/skia/infra/pathkit/test_pathkit.sh",
       "--builder",
diff --git a/infra/bots/recipes/test_pathkit.py b/infra/bots/recipes/test_pathkit.py
index d49627a..a1cfb98 100644
--- a/infra/bots/recipes/test_pathkit.py
+++ b/infra/bots/recipes/test_pathkit.py
@@ -6,6 +6,7 @@
 
 DEPS = [
   'checkout',
+  'docker',
   'env',
   'infra',
   'recipe_engine/file',
@@ -19,17 +20,13 @@
 
 
 DOCKER_IMAGE = 'gcr.io/skia-public/gold-karma-chrome-tests:77.0.3865.120_v2'
-INNER_KARMA_SCRIPT = '/SRC/skia/infra/pathkit/test_pathkit.sh'
+INNER_KARMA_SCRIPT = 'skia/infra/pathkit/test_pathkit.sh'
 
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
+  checkout_root = api.path['start_dir']
   out_dir = api.vars.swarming_out_dir
-  api.checkout.bot_update(checkout_root=checkout_root)
-
-  # Make sure this exists, otherwise Docker will make it with root permissions.
-  api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
 
   # The karma script is configured to look in ./npm-(asmjs|wasm)/bin/test/ for
   # the test files to load, so we must copy them there (see Set up for docker).
@@ -49,87 +46,47 @@
     else:
       bundle_name = 'pathkit.js.mem'
 
-  api.python.inline(
-      name='Set up for docker',
-      program='''import errno
-import os
-import shutil
-import sys
+  copies = {
+    base_dir.join('pathkit.js'): copy_dest.join('pathkit.js'),
+  }
+  if bundle_name:
+    copies[base_dir.join(bundle_name)] = copy_dest.join(bundle_name)
+  recursive_read = [checkout_root.join('skia')]
 
-copy_dest = sys.argv[1]
-base_dir = sys.argv[2]
-bundle_name = sys.argv[3]
-out_dir = sys.argv[4]
-
-# Clean out old binaries (if any)
-try:
-  shutil.rmtree(copy_dest)
-except OSError as e:
-  if e.errno != errno.ENOENT:
-    raise
-
-# Make folder
-try:
-  os.makedirs(copy_dest)
-except OSError as e:
-  if e.errno != errno.EEXIST:
-    raise
-
-# Copy binaries (pathkit.js and pathkit.wasm) to where the karma tests
-# expect them ($SKIA_ROOT/modules/pathkit/npm-wasm/bin/test/)
-dest = os.path.join(copy_dest, 'pathkit.js')
-shutil.copyfile(os.path.join(base_dir, 'pathkit.js'), dest)
-os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-if bundle_name:
-  dest = os.path.join(copy_dest, bundle_name)
-  shutil.copyfile(os.path.join(base_dir, bundle_name), dest)
-  os.chmod(dest, 0o644) # important, otherwise non-privileged docker can't read.
-
-# Prepare output folder, api.file.ensure_directory doesn't touch
-# the permissions of the out directory if it already exists.
-os.chmod(out_dir, 0o777) # important, otherwise non-privileged docker can't write.
-''',
-      args=[copy_dest, base_dir, bundle_name, out_dir],
-      infra_step=True)
-
-
-  cmd = ['docker', 'run', '--shm-size=2gb', '--rm',
-         '--volume', '%s:/SRC' % checkout_root,
-         '--volume', '%s:/OUT' % out_dir]
-
+  docker_args = None
   if 'asmjs' in api.vars.builder_name:
-    cmd.extend(['--env', 'ASM_JS=1'])
+    docker_args = ['--env', 'ASM_JS=1']
 
-  cmd.extend([
-      DOCKER_IMAGE,             INNER_KARMA_SCRIPT,
-      '--builder',              api.vars.builder_name,
-      '--git_hash',             api.properties['revision'],
-      '--buildbucket_build_id', api.properties.get('buildbucket_build_id',
-                                                  ''),
-      '--bot_id',               api.vars.swarming_bot_id,
-      '--task_id',              api.vars.swarming_task_id,
-      '--browser',              'Chrome',
-      '--config',               api.vars.configuration,
-      '--source_type',          'pathkit',
-      ])
-
+  args = [
+    '--builder',              api.vars.builder_name,
+    '--git_hash',             api.properties['revision'],
+    '--buildbucket_build_id', api.properties.get('buildbucket_build_id', ''),
+    '--bot_id',               api.vars.swarming_bot_id,
+    '--task_id',              api.vars.swarming_task_id,
+    '--browser',              'Chrome',
+    '--config',               api.vars.configuration,
+    '--source_type',          'pathkit',
+  ]
   if 'asmjs' in api.vars.builder_name:
-    cmd.extend(['--compiled_language', 'asmjs']) # the default is wasm
-
+    args.extend(['--compiled_language', 'asmjs']) # the default is wasm
   if api.vars.is_trybot:
-    cmd.extend([
+    args.extend([
       '--issue',         api.vars.issue,
       '--patchset',      api.vars.patchset,
     ])
 
-  # Override DOCKER_CONFIG set by Kitchen.
-  env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
-  with api.env(env):
-    api.run(
-        api.step,
-        'Test PathKit with Docker',
-        cmd=cmd)
+  api.docker.run(
+      name='Test PathKit with Docker',
+      docker_image=DOCKER_IMAGE,
+      src_dir=checkout_root,
+      out_dir=out_dir,
+      script=checkout_root.join(INNER_KARMA_SCRIPT),
+      args=args,
+      docker_args=docker_args,
+      copies=copies,
+      recursive_read=recursive_read,
+      attempts=3,
+  )
 
 
 def GenTests(api):
diff --git a/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json b/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json
index f444357..2639d30 100644
--- a/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json
+++ b/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json
@@ -1,105 +1,11 @@
 [
   {
     "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/cache/work"
-    ],
-    "infra_step": true,
-    "name": "makedirs checkout_path"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "remove",
-      "[START_DIR]/cache/work/.gclient_entries"
-    ],
-    "infra_step": true,
-    "name": "remove [START_DIR]/cache/work/.gclient_entries"
-  },
-  {
-    "cmd": [
-      "python",
-      "-u",
-      "RECIPE_MODULE[depot_tools::bot_update]/resources/bot_update.py",
-      "--spec-path",
-      "cache_dir = '[START_DIR]/cache/git'\nsolutions = [{'deps_file': '.DEPS.git', 'managed': False, 'name': 'skia', 'url': 'https://skia.googlesource.com/skia.git'}]",
-      "--patch_root",
-      "skia",
-      "--revision_mapping_file",
-      "{\"got_revision\": \"skia\"}",
-      "--git-cache-dir",
-      "[START_DIR]/cache/git",
-      "--cleanup-dir",
-      "[CLEANUP]/bot_update",
-      "--output_json",
-      "/path/to/tmp/json",
-      "--revision",
-      "skia@abc123"
-    ],
-    "cwd": "[START_DIR]/cache/work",
-    "env_suffixes": {
-      "PATH": [
-        "RECIPE_REPO[depot_tools]"
-      ]
-    },
-    "infra_step": true,
-    "name": "bot_update",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Some step text@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"did_run\": true, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"fixed_revisions\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"repository\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@    }@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_failure\": false, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"patch_root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\", @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"got_revision_cp\": \"refs/heads/master@{#164710}\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"root\": \"skia\", @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"source_manifest\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"directories\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@      \"skia\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"git_checkout\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@          \"repo_url\": \"https://fake.org/skia.git\", @@@",
-      "@@@STEP_LOG_LINE@json.output@          \"revision\": \"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@STEP_LOG_LINE@json.output@        }@@@",
-      "@@@STEP_LOG_LINE@json.output@      }@@@",
-      "@@@STEP_LOG_LINE@json.output@    }, @@@",
-      "@@@STEP_LOG_LINE@json.output@    \"version\": 0@@@",
-      "@@@STEP_LOG_LINE@json.output@  }, @@@",
-      "@@@STEP_LOG_LINE@json.output@  \"step_text\": \"Some step text\"@@@",
-      "@@@STEP_LOG_LINE@json.output@}@@@",
-      "@@@STEP_LOG_END@json.output@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision@\"9046e2e693bb92a76e972b694580e5d17ad10748\"@@@",
-      "@@@SET_BUILD_PROPERTY@got_revision_cp@\"refs/heads/master@{#164710}\"@@@"
-    ]
-  },
-  {
-    "cmd": [
       "python",
       "-u",
       "\nimport os\nimport subprocess\nimport sys\n\ncontainer_name = sys.argv[1]\ncheckout_root = sys.argv[2]\napk_location = sys.argv[3]\nDOCKER_IMAGE = sys.argv[4]\n\nMAX_TRIES = 5\n\nstart_cmd = ['docker', 'run', '--privileged', '--rm', '-d', # detached/daemon\n             '--name', container_name,\n             '--env', 'DEVICE=Samsung Galaxy S6',\n             '--volume', '%s:/SRC' % checkout_root,\n             '--volume', '%s:/OUT' % apk_location,\n             DOCKER_IMAGE]\n\nwait_cmd = ['docker', 'exec', container_name,\n            'timeout', '45', 'adb', 'wait-for-device']\n\nfor t in range(MAX_TRIES):\n  print 'Starting Emulator try %d' % t\n  try:\n    # Start emulator\n    print subprocess.check_output(start_cmd)\n    # Wait a short time using adb-wait-for-device\n    print subprocess.check_output(wait_cmd)\n    # if exit code 0, we are good so end loop\n    print 'Emulator started'\n    sys.exit(0)\n  except subprocess.CalledProcessError:\n    # else kill docker container\n    print 'Killing and trying again'\n    print subprocess.check_output(['docker', 'kill', container_name])\nprint 'Could not start emulator'\nsys.exit(1)\n",
       "android_em",
-      "[START_DIR]/cache/work",
+      "[START_DIR]",
       "[START_DIR]/build",
       "butomo1989/docker-android-x86-8.1@sha256:ad75c888e373d9ea7a2821fd8f64b53c9a22b5827e6fa516b396739a20b9bb88"
     ],
diff --git a/infra/bots/recipes/test_skqp_emulator.py b/infra/bots/recipes/test_skqp_emulator.py
index a8915f4..d4c0e8b 100644
--- a/infra/bots/recipes/test_skqp_emulator.py
+++ b/infra/bots/recipes/test_skqp_emulator.py
@@ -24,8 +24,7 @@
 
 def RunSteps(api):
   api.vars.setup()
-  checkout_root = api.checkout.default_checkout_root
-  api.checkout.bot_update(checkout_root=checkout_root)
+  checkout_root = api.path['start_dir']
 
   # This is where the APK should be, that is, where Swarming puts the inputs.
   apk_location = api.vars.build_dir
diff --git a/infra/bots/recipes/upload_calmbench_results.expected/normal_bot.json b/infra/bots/recipes/upload_calmbench_results.expected/normal_bot.json
deleted file mode 100644
index 7b648b1..0000000
--- a/infra/bots/recipes/upload_calmbench_results.expected/normal_bot.json
+++ /dev/null
@@ -1,80 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "glob",
-      "[START_DIR]/perf",
-      "*.json"
-    ],
-    "infra_step": true,
-    "name": "find json results",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@glob@[START_DIR]/perf/bench_modified_master.json@@@",
-      "@@@STEP_LOG_END@glob@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "glob",
-      "[START_DIR]/perf",
-      "*.csv"
-    ],
-    "infra_step": true,
-    "name": "find csv results",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@glob@[START_DIR]/perf/bench_modified_master.csv@@@",
-      "@@@STEP_LOG_END@glob@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json",
-      "[START_DIR]/perf/bench_modified_master.json",
-      "gs://skia-calmbench/calmbench-v1/2012/05/14/12/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All/bench_modified_master_abc123_1337000001.json"
-    ],
-    "infra_step": true,
-    "name": "upload json"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "csv",
-      "[START_DIR]/perf/bench_modified_master.csv",
-      "gs://skia-calmbench/calmbench-v1/2012/05/14/12/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All/bench_modified_master_abc123_1337000001.csv"
-    ],
-    "infra_step": true,
-    "name": "upload csv"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/upload_calmbench_results.expected/trybot.json b/infra/bots/recipes/upload_calmbench_results.expected/trybot.json
deleted file mode 100644
index 332e9a7..0000000
--- a/infra/bots/recipes/upload_calmbench_results.expected/trybot.json
+++ /dev/null
@@ -1,80 +0,0 @@
-[
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "ensure-directory",
-      "--mode",
-      "0777",
-      "[START_DIR]/tmp"
-    ],
-    "infra_step": true,
-    "name": "makedirs tmp_dir"
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "glob",
-      "[START_DIR]/perf",
-      "*.json"
-    ],
-    "infra_step": true,
-    "name": "find json results",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@glob@[START_DIR]/perf/bench_modified_master.json@@@",
-      "@@@STEP_LOG_END@glob@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "glob",
-      "[START_DIR]/perf",
-      "*.csv"
-    ],
-    "infra_step": true,
-    "name": "find csv results",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@glob@[START_DIR]/perf/bench_modified_master.csv@@@",
-      "@@@STEP_LOG_END@glob@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "json",
-      "[START_DIR]/perf/bench_modified_master.json",
-      "gs://skia-calmbench/trybot/calmbench-v1/2012/05/14/12/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All/456789/12/bench_modified_master_abc123_1337000001.json"
-    ],
-    "infra_step": true,
-    "name": "upload json"
-  },
-  {
-    "cmd": [
-      "gsutil",
-      "cp",
-      "-z",
-      "csv",
-      "[START_DIR]/perf/bench_modified_master.csv",
-      "gs://skia-calmbench/trybot/calmbench-v1/2012/05/14/12/Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All/456789/12/bench_modified_master_abc123_1337000001.csv"
-    ],
-    "infra_step": true,
-    "name": "upload csv"
-  },
-  {
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/infra/bots/recipes/upload_calmbench_results.py b/infra/bots/recipes/upload_calmbench_results.py
deleted file mode 100644
index 050e755..0000000
--- a/infra/bots/recipes/upload_calmbench_results.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-# Recipe for uploading calmbench results.
-
-
-import calendar
-
-
-DEPS = [
-  'flavor',
-  'recipe_engine/context',
-  'recipe_engine/file',
-  'recipe_engine/path',
-  'recipe_engine/properties',
-  'recipe_engine/step',
-  'recipe_engine/time',
-  'run',
-  'vars',
-]
-
-
-def FindFile(api, suffix):
-  with api.context(cwd=api.path['start_dir']):
-    results = api.file.glob_paths(
-        'find %s results' % suffix,
-        api.path['start_dir'].join('perf'),
-        '*.%s' % suffix,
-        test_data=['bench_modified_master.%s' % suffix])
-  if len(results) != 1:  # pragma: nocover
-    raise Exception('Unable to find the %s file!' % suffix)
-  return results[0]
-
-
-def RunSteps(api):
-  api.vars.setup()
-  api.file.ensure_directory('makedirs tmp_dir', api.vars.tmp_dir)
-  api.flavor.setup()
-
-  now = api.time.utcnow()
-
-  json_src = FindFile(api, "json")
-  csv_src = FindFile(api, "csv")
-
-  ts = int(calendar.timegm(now.utctimetuple()))
-  basename = "bench_modified_master_%s_%d" % (api.properties['revision'], ts)
-
-  gs_path = '/'.join((
-      'calmbench-v1', str(now.year).zfill(4),
-      str(now.month).zfill(2), str(now.day).zfill(2), str(now.hour).zfill(2),
-      api.vars.builder_name))
-
-  if api.vars.is_trybot:
-    gs_path = '/'.join(('trybot', gs_path,
-                        str(api.vars.issue), str(api.vars.patchset)))
-
-  dst = '/'.join((
-      'gs://%s' % api.properties['gs_bucket'], gs_path, basename))
-
-  json_dst = dst + ".json"
-  csv_dst = dst + ".csv"
-
-  api.step(
-      'upload json',
-      cmd=['gsutil', 'cp', '-z', 'json', json_src, json_dst],
-      infra_step=True)
-  api.step(
-      'upload csv',
-      cmd=['gsutil', 'cp', '-z', 'csv', csv_src, csv_dst],
-      infra_step=True)
-
-
-def GenTests(api):
-  builder = 'Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All'
-  yield (
-    api.test('normal_bot') +
-    api.properties(buildername=builder,
-                   repository='https://skia.googlesource.com/skia.git',
-                   gs_bucket='skia-calmbench',
-                   swarm_out_dir='[SWARM_OUT_DIR]',
-                   revision='abc123',
-                   path_config='kitchen')
-  )
-
-  yield (
-    api.test('trybot') +
-    api.properties.tryserver(
-        gerrit_project='skia',
-        gerrit_url='https://skia-review.googlesource.com/',
-    ) +
-    api.properties(buildername=builder,
-                   repository='https://skia.googlesource.com/skia.git',
-                   gs_bucket='skia-calmbench',
-                   swarm_out_dir='[SWARM_OUT_DIR]',
-                   revision='abc123',
-                   path_config='kitchen')
-  )
diff --git a/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json b/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json
index e2d2993..112ef71 100644
--- a/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json
+++ b/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json
@@ -51,7 +51,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/dm.json",
-      "gs://skia-infra-gm-alt/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
+      "gs://skia-infra-gm-alt/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
     ],
     "name": "upload dm.json"
   },
@@ -79,7 +79,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/verbose.log",
-      "gs://skia-infra-gm-alt/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
+      "gs://skia-infra-gm-alt/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
     ],
     "name": "upload verbose.log"
   },
diff --git a/infra/bots/recipes/upload_dm_results.expected/failed_once.json b/infra/bots/recipes/upload_dm_results.expected/failed_once.json
index 1c6aca1..c8d96a4 100644
--- a/infra/bots/recipes/upload_dm_results.expected/failed_once.json
+++ b/infra/bots/recipes/upload_dm_results.expected/failed_once.json
@@ -64,7 +64,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/dm.json",
-      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
+      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
     ],
     "name": "upload dm.json"
   },
@@ -92,7 +92,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/verbose.log",
-      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
+      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
     ],
     "name": "upload verbose.log"
   },
diff --git a/infra/bots/recipes/upload_dm_results.expected/normal_bot.json b/infra/bots/recipes/upload_dm_results.expected/normal_bot.json
index 1ebc1e4..96f97fd 100644
--- a/infra/bots/recipes/upload_dm_results.expected/normal_bot.json
+++ b/infra/bots/recipes/upload_dm_results.expected/normal_bot.json
@@ -51,7 +51,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/dm.json",
-      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
+      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/dm.json"
     ],
     "name": "upload dm.json"
   },
@@ -79,7 +79,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/verbose.log",
-      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
+      "gs://skia-infra-gm/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/verbose.log"
     ],
     "name": "upload verbose.log"
   },
diff --git a/infra/bots/recipes/upload_dm_results.expected/trybot.json b/infra/bots/recipes/upload_dm_results.expected/trybot.json
index ff71c92..aa301ad 100644
--- a/infra/bots/recipes/upload_dm_results.expected/trybot.json
+++ b/infra/bots/recipes/upload_dm_results.expected/trybot.json
@@ -51,7 +51,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/dm.json",
-      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/456789/12/dm.json"
+      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/456789/12/dm.json"
     ],
     "name": "upload dm.json"
   },
@@ -79,7 +79,7 @@
       "cp",
       "-Z",
       "[START_DIR]/test/verbose.log",
-      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/456789/12/verbose.log"
+      "gs://skia-infra-gm/trybot/dm-json-v1/2012/05/14/12/abc123/Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All/1337000001/456789/12/verbose.log"
     ],
     "name": "upload verbose.log"
   },
diff --git a/infra/bots/recipes/upload_dm_results.py b/infra/bots/recipes/upload_dm_results.py
index 3e8bfe4..34ade3d 100644
--- a/infra/bots/recipes/upload_dm_results.py
+++ b/infra/bots/recipes/upload_dm_results.py
@@ -81,7 +81,7 @@
 
 
 def GenTests(api):
-  builder = 'Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All'
+  builder = 'Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All'
   yield (
     api.test('normal_bot') +
     api.properties(buildername=builder,
diff --git a/infra/bots/recipes/upload_nano_results.expected/normal_bot.json b/infra/bots/recipes/upload_nano_results.expected/normal_bot.json
index 93c1e88..ee9532c 100644
--- a/infra/bots/recipes/upload_nano_results.expected/normal_bot.json
+++ b/infra/bots/recipes/upload_nano_results.expected/normal_bot.json
@@ -25,7 +25,7 @@
       "-z",
       "json",
       "[START_DIR]/perf/nanobench_abc123.json",
-      "gs://skia-perf/nano-json-v1/2012/05/14/12/Perf-Debian9-GCC-GCE-CPU-AVX2-x86_64-All-Debug/nanobench_abc123.json"
+      "gs://skia-perf/nano-json-v1/2012/05/14/12/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-All-Debug/nanobench_abc123.json"
     ],
     "infra_step": true,
     "name": "upload"
diff --git a/infra/bots/recipes/upload_nano_results.expected/trybot.json b/infra/bots/recipes/upload_nano_results.expected/trybot.json
index bd2e454..fd2f2f0 100644
--- a/infra/bots/recipes/upload_nano_results.expected/trybot.json
+++ b/infra/bots/recipes/upload_nano_results.expected/trybot.json
@@ -25,7 +25,7 @@
       "-z",
       "json",
       "[START_DIR]/perf/nanobench_abc123.json",
-      "gs://skia-perf/trybot/nano-json-v1/2012/05/14/12/Perf-Debian9-GCC-GCE-CPU-AVX2-x86_64-All-Debug/456789/12/nanobench_abc123.json"
+      "gs://skia-perf/trybot/nano-json-v1/2012/05/14/12/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-All-Debug/456789/12/nanobench_abc123.json"
     ],
     "infra_step": true,
     "name": "upload"
diff --git a/infra/bots/recipes/upload_nano_results.py b/infra/bots/recipes/upload_nano_results.py
index 9c108f8..4ff168d 100644
--- a/infra/bots/recipes/upload_nano_results.py
+++ b/infra/bots/recipes/upload_nano_results.py
@@ -53,7 +53,7 @@
 
 
 def GenTests(api):
-  builder = 'Perf-Debian9-GCC-GCE-CPU-AVX2-x86_64-All-Debug'
+  builder = 'Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-All-Debug'
   yield (
     api.test('normal_bot') +
     api.properties(buildername=builder,
diff --git a/infra/bots/run_recipe.py b/infra/bots/run_recipe.py
index 90ffdbe..ef56ed1 100755
--- a/infra/bots/run_recipe.py
+++ b/infra/bots/run_recipe.py
@@ -31,7 +31,6 @@
     '-known-gerrit-host', 'llvm.googlesource.com',
     '-known-gerrit-host', 'skia.googlesource.com',
     '-known-gerrit-host', 'webrtc.googlesource.com',
-    '-workdir', '.',
     '-recipe', sys.argv[2],
     '-properties', sys.argv[3],
     '-logdog-annotation-url', logdog_url,
diff --git a/infra/bots/skottie_wasm.isolate b/infra/bots/skottie_wasm.isolate
new file mode 100644
index 0000000..641e5f5
--- /dev/null
+++ b/infra/bots/skottie_wasm.isolate
@@ -0,0 +1,10 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../../tools/skottie-wasm-perf',
+    ],
+  },
+}
diff --git a/infra/bots/skqp.isolate b/infra/bots/skqp.isolate
new file mode 100644
index 0000000..aab0000
--- /dev/null
+++ b/infra/bots/skqp.isolate
@@ -0,0 +1,10 @@
+{
+  'includes': [
+    'swarm_recipe.isolate',
+  ],
+  'variables': {
+    'files': [
+      '../skqp',
+    ],
+  },
+}
diff --git a/infra/bots/tasks.json b/infra/bots/tasks.json
index eedc484..b5c151e 100755
--- a/infra/bots/tasks.json
+++ b/infra/bots/tasks.json
@@ -1,5 +1,60 @@
 {
   "jobs": {
+    "Build-Debian10-GCC-loongson3a-Debug-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-loongson3a-Debug-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-loongson3a-Release-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-loongson3a-Release-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-mips64el-Debug-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-mips64el-Debug-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-mips64el-Release-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-mips64el-Release-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86-Debug-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86-Debug-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86-Release-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86-Release-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86_64-Debug-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86_64-Debug-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86_64-Debug-NoGPU_Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86_64-Debug-NoGPU_Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86_64-Release-Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86_64-Release-Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86_64-Release-NoGPU_Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86_64-Release-NoGPU_Docker"
+      ]
+    },
+    "Build-Debian10-GCC-x86_64-Release-Shared_Docker": {
+      "tasks": [
+        "Build-Debian10-GCC-x86_64-Release-Shared_Docker"
+      ]
+    },
     "Build-Debian9-Clang-TAP-Presubmit-G3_Framework": {
       "tasks": [
         "Build-Debian9-Clang-TAP-Presubmit-G3_Framework"
@@ -97,6 +152,11 @@
         "Build-Debian9-Clang-arm64-Release-Android_Vulkan"
       ]
     },
+    "Build-Debian9-Clang-arm64-Release-Android_Wuffs": {
+      "tasks": [
+        "Build-Debian9-Clang-arm64-Release-Android_Wuffs"
+      ]
+    },
     "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework": {
       "tasks": [
         "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework"
@@ -264,11 +324,6 @@
         "Build-Debian9-Clang-x86_64-Release-NoDEPS"
       ]
     },
-    "Build-Debian9-Clang-x86_64-Release-ParentRevision": {
-      "tasks": [
-        "Build-Debian9-Clang-x86_64-Release-ParentRevision"
-      ]
-    },
     "Build-Debian9-Clang-x86_64-Release-SKNX_NO_SIMD": {
       "tasks": [
         "Build-Debian9-Clang-x86_64-Release-SKNX_NO_SIMD"
@@ -289,6 +344,11 @@
         "Build-Debian9-Clang-x86_64-Release-SK_FORCE_RASTER_PIPELINE_BLITTER"
       ]
     },
+    "Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER": {
+      "tasks": [
+        "Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER"
+      ]
+    },
     "Build-Debian9-Clang-x86_64-Release-Static": {
       "tasks": [
         "Build-Debian9-Clang-x86_64-Release-Static"
@@ -359,66 +419,6 @@
         "Build-Debian9-EMCC-wasm-Release-PathKit"
       ]
     },
-    "Build-Debian9-GCC-loongson3a-Debug": {
-      "tasks": [
-        "Build-Debian9-GCC-loongson3a-Debug"
-      ]
-    },
-    "Build-Debian9-GCC-loongson3a-Release": {
-      "tasks": [
-        "Build-Debian9-GCC-loongson3a-Release"
-      ]
-    },
-    "Build-Debian9-GCC-mips64el-Debug": {
-      "tasks": [
-        "Build-Debian9-GCC-mips64el-Debug"
-      ]
-    },
-    "Build-Debian9-GCC-mips64el-Release": {
-      "tasks": [
-        "Build-Debian9-GCC-mips64el-Release"
-      ]
-    },
-    "Build-Debian9-GCC-x86-Debug": {
-      "tasks": [
-        "Build-Debian9-GCC-x86-Debug"
-      ]
-    },
-    "Build-Debian9-GCC-x86-Release": {
-      "tasks": [
-        "Build-Debian9-GCC-x86-Release"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Debug": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Debug"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Debug-NoGPU": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Debug-NoGPU"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Release": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Release"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Release-NoGPU": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Release-NoGPU"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41"
-      ]
-    },
-    "Build-Debian9-GCC-x86_64-Release-Shared": {
-      "tasks": [
-        "Build-Debian9-GCC-x86_64-Release-Shared"
-      ]
-    },
     "Build-Mac-Clang-arm-Debug-iOS": {
       "tasks": [
         "Build-Mac-Clang-arm-Debug-iOS"
@@ -717,16 +717,6 @@
         "Upload-BuildStats-Debian9-EMCC-wasm-Release-PathKit"
       ]
     },
-    "Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All": {
-      "tasks": [
-        "Upload-Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All"
-      ]
-    },
-    "Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All": {
-      "tasks": [
-        "Upload-Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All"
-      ]
-    },
     "Housekeeper-Nightly-RecreateSKPs_Canary": {
       "tasks": [
         "Housekeeper-Nightly-RecreateSKPs_Canary"
@@ -892,6 +882,11 @@
         "Upload-Perf-Android-Clang-Nexus5-GPU-Adreno330-arm-Release-All-Android"
       ]
     },
+    "Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs": {
+      "tasks": [
+        "Upload-Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs"
+      ]
+    },
     "Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android": {
       "tasks": [
         "Upload-Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android"
@@ -947,74 +942,39 @@
         "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android"
       ]
     },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench"
-      ]
-    },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench"
-      ]
-    },
     "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan": {
       "tasks": [
         "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan"
       ]
     },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench"
-      ]
-    },
     "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android": {
       "tasks": [
         "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android"
       ]
     },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench"
-      ]
-    },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench"
-      ]
-    },
     "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan": {
       "tasks": [
         "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan"
       ]
     },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench"
-      ]
-    },
     "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android": {
       "tasks": [
         "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android"
       ]
     },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench"
-      ]
-    },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench": {
-      "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench"
-      ]
-    },
     "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan": {
       "tasks": [
         "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan"
       ]
     },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench": {
+    "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
       "tasks": [
-        "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench"
+        "Upload-Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android"
+      ]
+    },
+    "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "tasks": [
+        "Upload-Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan"
       ]
     },
     "Perf-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Release-All-Android": {
@@ -1215,14 +1175,14 @@
       ],
       "trigger": "master"
     },
-    "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+    "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
       "tasks": [
-        "Upload-Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
+        "Upload-Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
       ]
     },
-    "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
+    "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
       "tasks": [
-        "Upload-Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer"
+        "Upload-Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer"
       ],
       "trigger": "master"
     },
@@ -1236,6 +1196,12 @@
         "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ASAN"
       ]
     },
+    "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
+      "priority": 0.085,
+      "tasks": [
+        "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41"
+      ]
+    },
     "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan": {
       "tasks": [
         "Upload-Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan"
@@ -1246,12 +1212,6 @@
         "Upload-Perf-Ubuntu18-EMCC-Golo-GPU-QuadroP400-wasm-Release-All-SkottieWASM"
       ]
     },
-    "Perf-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
-      "priority": 0.085,
-      "tasks": [
-        "Perf-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41"
-      ]
-    },
     "Perf-Ubuntu18-none-Golo-GPU-QuadroP400-x86_64-Release-All-LottieWeb": {
       "tasks": [
         "Upload-Perf-Ubuntu18-none-Golo-GPU-QuadroP400-x86_64-Release-All-LottieWeb"
@@ -1832,6 +1792,26 @@
         "Upload-Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan"
       ]
     },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android": {
+      "tasks": [
+        "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan": {
+      "tasks": [
+        "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
+      "tasks": [
+        "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "tasks": [
+        "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan"
+      ]
+    },
     "Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android": {
       "tasks": [
         "Upload-Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android"
@@ -1922,6 +1902,26 @@
         "Upload-Test-Chromecast-Clang-Chorizo-GPU-Cortex_A7-arm-Release-All"
       ]
     },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker": {
+      "tasks": [
+        "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker": {
+      "tasks": [
+        "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker": {
+      "tasks": [
+        "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker": {
+      "tasks": [
+        "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker"
+      ]
+    },
     "Test-Debian9-Clang-GCE-CPU-AVX2-x86-Debug-All": {
       "tasks": [
         "Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86-Debug-All"
@@ -2012,6 +2012,11 @@
         "Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_FORCE_RASTER_PIPELINE_BLITTER"
       ]
     },
+    "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER": {
+      "tasks": [
+        "Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER"
+      ]
+    },
     "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN": {
       "tasks": [
         "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN"
@@ -2187,26 +2192,6 @@
         "Upload-Test-Debian9-EMCC-GCE-GPU-AVX2-wasm-Release-All-CanvasKit"
       ]
     },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All": {
-      "tasks": [
-        "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All": {
-      "tasks": [
-        "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All": {
-      "tasks": [
-        "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All": {
-      "tasks": [
-        "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All"
-      ]
-    },
     "Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All": {
       "tasks": [
         "Upload-Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All"
@@ -2360,37 +2345,42 @@
         "Upload-Test-Mac10.13-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts"
       ]
     },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
-      "tasks": [
-        "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
-      "tasks": [
-        "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer"
-      ],
-      "trigger": "master"
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
-      "tasks": [
-        "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
-      "tasks": [
-        "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
-      "tasks": [
-        "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal"
-      ]
-    },
     "Test-Mac10.14-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts": {
       "tasks": [
         "Upload-Test-Mac10.14-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts"
       ]
     },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer"
+      ],
+      "trigger": "master"
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
+      "tasks": [
+        "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal"
+      ]
+    },
     "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All": {
       "tasks": [
         "Upload-Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All"
@@ -2461,29 +2451,29 @@
         "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-PreAbandonGpuContext"
       ]
     },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41": {
+      "priority": 0.085,
+      "tasks": [
+        "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41"
+      ]
+    },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41": {
+      "priority": 0.085,
+      "tasks": [
+        "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41"
+      ]
+    },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
+      "priority": 0.085,
+      "tasks": [
+        "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41"
+      ]
+    },
     "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan": {
       "tasks": [
         "Upload-Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan"
       ]
     },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41": {
-      "priority": 0.085,
-      "tasks": [
-        "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41"
-      ]
-    },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41": {
-      "priority": 0.085,
-      "tasks": [
-        "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41"
-      ]
-    },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
-      "priority": 0.085,
-      "tasks": [
-        "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41"
-      ]
-    },
     "Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All": {
       "tasks": [
         "Upload-Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All"
@@ -3011,28 +3001,814 @@
     }
   },
   "tasks": {
-    "Build-Debian9-Clang-TAP-Presubmit-G3_Framework": {
+    "Build-Debian10-GCC-loongson3a-Debug-Docker": {
       "caches": [
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-loongson3a-Debug-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-loongson3a-Release-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-loongson3a-Release-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-mips64el-Debug-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-mips64el-Debug-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-mips64el-Release-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-mips64el-Release-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86-Debug-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86-Debug-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86-Release-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86-Release-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86_64-Debug-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86_64-Debug-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86_64-Debug-NoGPU_Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86_64-Debug-NoGPU_Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86_64-Release-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86_64-Release-Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86_64-Release-NoGPU_Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86_64-Release-NoGPU_Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian10-GCC-x86_64-Release-Shared_Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "docker",
+          "path": "cache/docker"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian10-GCC-x86_64-Release-Shared_Docker\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian9-Clang-TAP-Presubmit-G3_Framework": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -3069,7 +3845,7 @@
       "io_timeout_ns": 10800000000000,
       "isolate": "compile_g3_framework.isolate",
       "max_attempts": 1,
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+      "service_account": "skia-g3-framework-compile@skia-swarming-bots.iam.gserviceaccount.com"
     },
     "Build-Debian9-Clang-arm-Debug-Android": {
       "caches": [
@@ -3082,17 +3858,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3151,17 +3927,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3220,17 +3996,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -3299,17 +4075,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/cast_toolchain",
@@ -3373,17 +4149,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3442,17 +4218,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3511,17 +4287,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3580,17 +4356,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3649,17 +4425,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3718,17 +4494,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -3797,17 +4573,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/cast_toolchain",
@@ -3883,32 +4659,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -3966,17 +4742,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4035,17 +4811,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4104,17 +4880,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4173,17 +4949,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4242,17 +5018,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4311,17 +5087,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4369,7 +5145,7 @@
       ],
       "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework": {
+    "Build-Debian9-Clang-arm64-Release-Android_Wuffs": {
       "caches": [
         {
           "name": "vpython",
@@ -4380,17 +5156,113 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/android_ndk_linux",
+          "path": "android_ndk_linux",
+          "version": "version:15"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-Clang-arm64-Release-Android_Wuffs\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highcpu-64",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Build-Debian9-Clang-cf_x86_phone-eng-Android_Framework": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -4427,30 +5299,57 @@
       "io_timeout_ns": 7200000000000,
       "isolate": "compile_android_framework.isolate",
       "max_attempts": 1,
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+      "service_account": "skia-android-framework-compile@skia-swarming-bots.iam.gserviceaccount.com"
     },
     "Build-Debian9-Clang-host-sdk-Android_Framework": {
       "caches": [
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -4487,7 +5386,7 @@
       "io_timeout_ns": 7200000000000,
       "isolate": "compile_android_framework.isolate",
       "max_attempts": 1,
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+      "service_account": "skia-android-framework-compile@skia-swarming-bots.iam.gserviceaccount.com"
     },
     "Build-Debian9-Clang-x64-Debug-Android": {
       "caches": [
@@ -4500,17 +5399,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4569,17 +5468,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4638,17 +5537,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -4707,17 +5606,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4776,17 +5675,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4845,17 +5744,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4914,17 +5813,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_linux",
@@ -4999,32 +5898,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -5077,17 +5976,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5146,17 +6045,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5215,17 +6114,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5284,17 +6183,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5358,17 +6257,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5427,17 +6326,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5506,17 +6405,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5575,17 +6474,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5644,17 +6543,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5713,17 +6612,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5782,17 +6681,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5856,17 +6755,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5930,17 +6829,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -5999,17 +6898,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6068,17 +6967,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6137,17 +7036,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6206,17 +7105,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6275,17 +7174,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6344,17 +7243,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6429,32 +7328,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6513,17 +7412,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6587,17 +7486,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6664,32 +7563,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6736,101 +7635,6 @@
       ],
       "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Build-Debian9-Clang-x86_64-Release-ParentRevision": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        },
-        {
-          "name": "git",
-          "path": "cache/git"
-        },
-        {
-          "name": "git_cache",
-          "path": "cache/git_cache"
-        },
-        {
-          "name": "work",
-          "path": "cache/work"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "skia/bots/clang_linux",
-          "path": "clang_linux",
-          "version": "version:14"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "sync_and_compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Build-Debian9-Clang-x86_64-Release-ParentRevision\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"build\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Build-Debian9-Clang-x86_64-Release-SKNX_NO_SIMD": {
       "caches": [
         {
@@ -6842,17 +7646,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6911,17 +7715,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -6980,17 +7784,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7049,17 +7853,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7107,6 +7911,75 @@
       ],
       "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:14"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "compile",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER\",\"swarm_out_dir\":\"build\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highcpu-64",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "idempotent": true,
+      "io_timeout_ns": 3600000000000,
+      "isolate": "compile.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "build"
+      ],
+      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Build-Debian9-Clang-x86_64-Release-Static": {
       "caches": [
         {
@@ -7118,17 +7991,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7187,17 +8060,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7261,17 +8134,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7330,17 +8203,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7399,17 +8272,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7468,17 +8341,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/clang_linux",
@@ -7541,17 +8414,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7610,17 +8483,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7679,17 +8552,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7748,17 +8621,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7817,17 +8690,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7886,17 +8759,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -7955,17 +8828,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -8024,17 +8897,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -8078,794 +8951,6 @@
       ],
       "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Build-Debian9-GCC-loongson3a-Debug": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/mips64el_toolchain_linux",
-          "path": "mips64el_toolchain_linux",
-          "version": "version:6"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-loongson3a-Debug\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-loongson3a-Release": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/mips64el_toolchain_linux",
-          "path": "mips64el_toolchain_linux",
-          "version": "version:6"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-loongson3a-Release\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-mips64el-Debug": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/mips64el_toolchain_linux",
-          "path": "mips64el_toolchain_linux",
-          "version": "version:6"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-mips64el-Debug\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-mips64el-Release": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/mips64el_toolchain_linux",
-          "path": "mips64el_toolchain_linux",
-          "version": "version:6"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-mips64el-Release\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86-Debug": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86-Debug\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86-Release": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86-Release\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Debug": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Debug\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Debug-NoGPU": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Debug-NoGPU\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Release": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Release\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Release-NoGPU": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Release-NoGPU\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Build-Debian9-GCC-x86_64-Release-Shared": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "compile",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildername\":\"Build-Debian9-GCC-x86_64-Release-Shared\",\"swarm_out_dir\":\"build\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highcpu-64",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "idempotent": true,
-      "io_timeout_ns": 3600000000000,
-      "isolate": "compile.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "build"
-      ],
-      "service_account": "skia-external-compile-tasks@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Build-Mac-Clang-arm-Debug-iOS": {
       "caches": [
         {
@@ -8881,17 +8966,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -8958,17 +9043,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9031,17 +9116,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/android_ndk_darwin",
@@ -9103,17 +9188,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9180,17 +9265,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9257,17 +9342,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9334,17 +9419,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9411,17 +9496,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9488,17 +9573,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9560,17 +9645,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9632,17 +9717,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9716,32 +9801,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9802,17 +9887,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9874,17 +9959,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -9946,17 +10031,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10018,17 +10103,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10102,32 +10187,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10188,17 +10273,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10260,17 +10345,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10332,17 +10417,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/mac_toolchain/${platform}",
@@ -10400,17 +10485,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10476,17 +10561,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10552,17 +10637,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10628,17 +10713,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10704,17 +10789,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10779,17 +10864,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10855,17 +10940,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -10931,17 +11016,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11007,17 +11092,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11083,17 +11168,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11159,17 +11244,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11240,17 +11325,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11316,17 +11401,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11392,17 +11477,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11468,17 +11553,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11544,17 +11629,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11620,17 +11705,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11696,17 +11781,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11772,17 +11857,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11848,17 +11933,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11919,17 +12004,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -11990,17 +12075,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12061,17 +12146,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12132,17 +12217,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12203,17 +12288,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12274,17 +12359,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12345,17 +12430,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12416,17 +12501,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12487,17 +12572,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12558,17 +12643,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12629,17 +12714,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -12694,28 +12779,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -12763,28 +12875,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -12832,28 +12971,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -12901,28 +13067,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -12970,28 +13163,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13039,28 +13259,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13108,28 +13355,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13177,28 +13451,55 @@
         {
           "name": "vpython",
           "path": "cache/vpython"
+        },
+        {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
         }
       ],
       "cipd_packages": [
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/bloaty",
           "path": "bloaty",
           "version": "version:1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13241,189 +13542,6 @@
         "perf"
       ]
     },
-    "Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        },
-        {
-          "name": "git",
-          "path": "cache/git"
-        },
-        {
-          "name": "git_cache",
-          "path": "cache/git_cache"
-        },
-        {
-          "name": "work",
-          "path": "cache/work"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "calmbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-x86_64-Release",
-        "Build-Debian9-Clang-x86_64-Release-ParentRevision",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateSVG"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "machine_type:n1-standard-16",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "calmbench.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
-    "Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        },
-        {
-          "name": "git",
-          "path": "cache/git"
-        },
-        {
-          "name": "git_cache",
-          "path": "cache/git_cache"
-        },
-        {
-          "name": "work",
-          "path": "cache/work"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "calmbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-x86_64-Release",
-        "Build-Debian9-Clang-x86_64-Release-ParentRevision",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateSVG"
-      ],
-      "dimensions": [
-        "gpu:10de:1cb3-430.14",
-        "os:Ubuntu-18.04",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "calmbench.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Housekeeper-Nightly-RecreateSKPs_Canary": {
       "caches": [
         {
@@ -13431,6 +13549,18 @@
           "path": "cache/vpython"
         },
         {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
+        },
+        {
           "name": "go_cache",
           "path": "cache/go_cache"
         },
@@ -13443,32 +13573,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
@@ -13524,17 +13654,17 @@
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
@@ -13618,32 +13748,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
@@ -13707,32 +13837,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13786,17 +13916,17 @@
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
@@ -13831,22 +13961,27 @@
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/python/cpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.7.15.chromium14"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -13877,6 +14012,14 @@
           "path": "cache/vpython"
         },
         {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
           "name": "work",
           "path": "cache/work"
         },
@@ -13893,22 +14036,42 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "version:2.24.0.chromium16"
+        },
+        {
+          "name": "infra/tools/git/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/git-credential-luci/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
           "path": "go",
           "version": "version:7"
+        },
+        {
+          "name": "skia/bots/clang_linux",
+          "path": "clang_linux",
+          "version": "version:14"
         }
       ],
       "command": [
@@ -13978,37 +14141,37 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
@@ -14084,17 +14247,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -14104,22 +14267,22 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go_win",
@@ -14217,7 +14380,7 @@
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         }
       ],
       "command": [
@@ -14265,7 +14428,7 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         }
       ],
       "command": [
@@ -14315,6 +14478,18 @@
           "path": "cache/vpython"
         },
         {
+          "name": "git",
+          "path": "cache/git"
+        },
+        {
+          "name": "git_cache",
+          "path": "cache/git_cache"
+        },
+        {
+          "name": "work",
+          "path": "cache/work"
+        },
+        {
           "name": "go_cache",
           "path": "cache/go_cache"
         },
@@ -14327,32 +14502,32 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
+          "version": "version:2.24.0.chromium16"
         },
         {
           "name": "infra/tools/git/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/git-credential-luci/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/go",
@@ -14404,17 +14579,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14470,17 +14645,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/lottie-samples",
@@ -14541,17 +14716,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14607,17 +14782,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14673,17 +14848,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14739,17 +14914,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14805,17 +14980,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14871,17 +15046,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -14937,17 +15112,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15003,17 +15178,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/text_blob_traces",
+          "path": "text_blob_traces",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -15069,17 +15249,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/text_blob_traces",
+          "path": "text_blob_traces",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -15135,17 +15320,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15201,17 +15386,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15267,17 +15452,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15333,17 +15518,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15399,17 +15584,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15465,17 +15650,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/text_blob_traces",
+          "path": "text_blob_traces",
+          "version": "version:0"
         }
       ],
       "command": [
@@ -15531,17 +15721,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15586,6 +15776,77 @@
         "perf"
       ]
     },
+    "Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/text_blob_traces",
+          "path": "text_blob_traces",
+          "version": "version:0"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "perf",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Release-Android_Wuffs",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:OPR6.170623.023",
+        "device_type:bullhead",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "perf_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "perf"
+      ]
+    },
     "Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android": {
       "caches": [
         {
@@ -15597,17 +15858,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15663,17 +15924,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15729,17 +15990,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -15800,17 +16061,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -15871,17 +16132,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -15937,17 +16198,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16002,17 +16263,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16067,17 +16328,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16132,17 +16393,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16198,17 +16459,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16263,17 +16524,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16318,136 +16579,6 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PPR1.180610.009",
-        "device_type:taimen",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PPR1.180610.009",
-        "device_type:taimen",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -16459,17 +16590,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16514,71 +16645,6 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PPR1.180610.009",
-        "device_type:taimen",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android": {
       "caches": [
         {
@@ -16590,17 +16656,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16645,136 +16711,6 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PQ1A.190105.004",
-        "device_type:blueline",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PQ1A.190105.004",
-        "device_type:blueline",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -16786,17 +16722,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16841,71 +16777,6 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:PQ1A.190105.004",
-        "device_type:blueline",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android": {
       "caches": [
         {
@@ -16917,17 +16788,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -16972,136 +16843,6 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:QP1A.190711.020",
-        "device_type:sargo",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android",
-        "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
-      ],
-      "dimensions": [
-        "device_os:QP1A.190711.020",
-        "device_type:sargo",
-        "os:Android",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -17113,17 +16854,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17168,7 +16909,7 @@
         "perf"
       ]
     },
-    "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench": {
+    "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
       "caches": [
         {
           "name": "vpython",
@@ -17179,36 +16920,37 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
         "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
-        "skpbench",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "perf",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
         "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
+        "Build-Debian9-Clang-arm64-Release-Android",
         "Housekeeper-PerCommit-IsolateSKP",
-        "Housekeeper-PerCommit-IsolateMSKP"
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
       ],
       "dimensions": [
-        "device_os:QP1A.190711.020",
-        "device_type:sargo",
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
         "os:Android",
         "pool:Skia"
       ],
@@ -17227,7 +16969,73 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "skpbench_skia_bundled.isolate",
+      "isolate": "perf_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "perf"
+      ]
+    },
+    "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "perf",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "perf_skia_bundled.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -17244,17 +17052,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17310,17 +17118,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17376,17 +17184,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17442,17 +17250,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17508,17 +17316,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17574,17 +17382,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17640,17 +17448,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17706,17 +17514,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17770,17 +17578,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -17834,27 +17642,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -17912,27 +17720,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -17995,27 +17803,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18073,27 +17881,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18151,27 +17959,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18229,27 +18037,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18312,27 +18120,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18390,27 +18198,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18467,27 +18275,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18549,27 +18357,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18636,27 +18444,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18718,27 +18526,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18805,27 +18613,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18892,27 +18700,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -18974,27 +18782,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19056,17 +18864,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -19102,7 +18910,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "pathkit.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19119,17 +18927,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -19165,7 +18973,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "canvaskit.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19182,17 +18990,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -19228,7 +19036,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "pathkit.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19245,27 +19053,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19281,21 +19089,6 @@
           "name": "skia/bots/lottie-samples",
           "path": "lottie-samples",
           "version": "version:1"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
         }
       ],
       "command": [
@@ -19331,7 +19124,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "skottie_wasm.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19348,17 +19141,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -19396,7 +19189,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "canvaskit.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19413,27 +19206,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19449,21 +19242,6 @@
           "name": "skia/bots/lottie-samples",
           "path": "lottie-samples",
           "version": "version:1"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
         }
       ],
       "command": [
@@ -19498,7 +19276,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "lottie_web.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -19515,27 +19293,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19592,27 +19370,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19669,27 +19447,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19746,27 +19524,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19823,27 +19601,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19900,27 +19678,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -19977,27 +19755,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20054,27 +19832,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20132,27 +19910,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20199,7 +19977,7 @@
         "perf"
       ]
     },
-    "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+    "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
       "caches": [
         {
           "name": "vpython",
@@ -20210,27 +19988,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20243,7 +20021,7 @@
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
         "perf",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
@@ -20252,7 +20030,7 @@
       ],
       "dimensions": [
         "gpu:8086:1626",
-        "os:Mac-10.14.3",
+        "os:Mac-10.15.1",
         "pool:Skia"
       ],
       "env_prefixes": {
@@ -20276,7 +20054,7 @@
         "perf"
       ]
     },
-    "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
+    "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
       "caches": [
         {
           "name": "vpython",
@@ -20287,27 +20065,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20320,7 +20098,7 @@
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
         "perf",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
@@ -20329,7 +20107,7 @@
       ],
       "dimensions": [
         "gpu:8086:1626",
-        "os:Mac-10.14.3",
+        "os:Mac-10.15.1",
         "pool:Skia"
       ],
       "env_prefixes": {
@@ -20364,27 +20142,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20441,27 +20219,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20512,6 +20290,89 @@
         "perf"
       ]
     },
+    "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "perf",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ],
+      "dimensions": [
+        "gpu:10de:1cb3-430.14",
+        "os:Ubuntu-18.04",
+        "pool:Skia",
+        "valgrind:1"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 32400000000000,
+      "expiration_ns": 172800000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 32400000000000,
+      "isolate": "perf_skia_bundled.isolate",
+      "max_attempts": 1,
+      "outputs": [
+        "perf"
+      ]
+    },
     "Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan": {
       "caches": [
         {
@@ -20523,27 +20384,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20605,27 +20466,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20641,21 +20502,6 @@
           "name": "skia/bots/lottie-samples",
           "path": "lottie-samples",
           "version": "version:1"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
         }
       ],
       "command": [
@@ -20690,95 +20536,12 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "skottie_wasm.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
       ]
     },
-    "Perf-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        },
-        {
-          "name": "skia/bots/valgrind",
-          "path": "valgrind",
-          "version": "version:7"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "perf",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"perf\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41"
-      ],
-      "dimensions": [
-        "gpu:10de:1cb3-430.14",
-        "os:Ubuntu-18.04",
-        "pool:Skia",
-        "valgrind:1"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 32400000000000,
-      "expiration_ns": 172800000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 32400000000000,
-      "isolate": "perf_skia_bundled.isolate",
-      "max_attempts": 1,
-      "outputs": [
-        "perf"
-      ]
-    },
     "Perf-Ubuntu18-none-Golo-GPU-QuadroP400-x86_64-Release-All-LottieWeb": {
       "caches": [
         {
@@ -20790,27 +20553,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20826,21 +20589,6 @@
           "name": "skia/bots/lottie-samples",
           "path": "lottie-samples",
           "version": "version:1"
-        },
-        {
-          "name": "infra/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "version:2.17.1.chromium15"
-        },
-        {
-          "name": "infra/tools/git/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:c9c8a52bfeaf8bc00ece22fdfd447822c8fcad77"
-        },
-        {
-          "name": "infra/tools/luci/git-credential-luci/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
         }
       ],
       "command": [
@@ -20874,7 +20622,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "perf_skia_bundled.isolate",
+      "isolate": "lottie_web.isolate",
       "max_attempts": 2,
       "outputs": [
         "perf"
@@ -20891,17 +20639,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -20911,12 +20659,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -20973,17 +20721,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -20993,12 +20741,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21055,17 +20803,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21075,12 +20823,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21137,17 +20885,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21157,12 +20905,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21219,17 +20967,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21239,12 +20987,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21301,17 +21049,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21321,12 +21069,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21383,17 +21131,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21403,12 +21151,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21465,17 +21213,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21485,12 +21233,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21547,17 +21295,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21616,17 +21364,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21685,17 +21433,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21754,17 +21502,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21774,12 +21522,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21836,17 +21584,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21856,12 +21604,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -21918,17 +21666,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -21938,12 +21686,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22000,17 +21748,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22020,12 +21768,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22082,17 +21830,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22102,12 +21850,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22164,17 +21912,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22184,12 +21932,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22246,17 +21994,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22266,12 +22014,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22328,17 +22076,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22348,12 +22096,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22410,17 +22158,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22430,12 +22178,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22492,17 +22240,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22512,12 +22260,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22574,17 +22322,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22594,12 +22342,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22656,17 +22404,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22676,12 +22424,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22738,17 +22486,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22758,12 +22506,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22820,17 +22568,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22840,12 +22588,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22902,17 +22650,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -22922,12 +22670,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -22984,17 +22732,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23004,12 +22752,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23066,17 +22814,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23086,12 +22834,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23148,17 +22896,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23168,12 +22916,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23230,17 +22978,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23250,12 +22998,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23312,17 +23060,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23332,12 +23080,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23394,17 +23142,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23414,12 +23162,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23476,17 +23224,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23496,12 +23244,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23560,17 +23308,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -23580,12 +23328,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -23644,17 +23392,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -23709,17 +23457,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -23774,17 +23522,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -23839,17 +23587,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -23904,17 +23652,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -23969,17 +23717,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24034,17 +23782,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24099,17 +23847,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24165,17 +23913,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24231,17 +23979,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24297,17 +24045,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24363,17 +24111,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24429,17 +24177,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24495,17 +24243,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24561,17 +24309,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24627,17 +24375,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24693,17 +24441,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24759,17 +24507,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24825,17 +24573,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24891,17 +24639,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -24957,17 +24705,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25023,17 +24771,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25089,17 +24837,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25155,17 +24903,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25221,17 +24969,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25287,17 +25035,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25353,17 +25101,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25419,17 +25167,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25485,17 +25233,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25551,17 +25299,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25617,17 +25365,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25683,17 +25431,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25749,17 +25497,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25815,17 +25563,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25881,17 +25629,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -25947,17 +25695,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26013,17 +25761,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26079,17 +25827,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26145,17 +25893,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26211,17 +25959,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26277,17 +26025,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26343,17 +26091,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26409,17 +26157,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26475,17 +26223,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26541,17 +26289,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26607,17 +26355,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26673,17 +26421,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26739,17 +26487,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -26805,17 +26553,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -26876,17 +26624,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -26947,17 +26695,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -27018,17 +26766,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -27089,17 +26837,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -27160,17 +26908,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -27231,17 +26979,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27298,17 +27046,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27365,17 +27113,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27432,17 +27180,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27499,17 +27247,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27565,17 +27313,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27631,17 +27379,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27697,17 +27445,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27763,17 +27511,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27830,17 +27578,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27897,17 +27645,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -27963,17 +27711,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28029,17 +27777,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28095,17 +27843,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28161,17 +27909,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28227,17 +27975,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28293,17 +28041,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28359,17 +28107,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28425,17 +28173,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28491,17 +28239,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28557,17 +28305,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28623,17 +28371,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28689,17 +28437,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28755,17 +28503,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28821,17 +28569,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28887,17 +28635,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -28953,17 +28701,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29008,6 +28756,270 @@
         "test"
       ]
     },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Debug-Android",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Debug-Android_Vulkan",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Release-Android",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-arm64-Release-Android_Vulkan",
+        "Housekeeper-PerCommit-IsolateSKP",
+        "Housekeeper-PerCommit-IsolateSVG",
+        "Housekeeper-PerCommit-IsolateSkImage"
+      ],
+      "dimensions": [
+        "device_os:QD1A.190821.011.C4",
+        "device_type:flame",
+        "os:Android",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
     "Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android": {
       "caches": [
         {
@@ -29019,17 +29031,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29085,17 +29097,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29151,17 +29163,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29217,17 +29229,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29283,17 +29295,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29349,17 +29361,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29415,17 +29427,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29481,17 +29493,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29547,17 +29559,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29613,17 +29625,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29679,17 +29691,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29745,17 +29757,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29811,17 +29823,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29877,17 +29889,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -29943,17 +29955,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -30007,17 +30019,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -30071,17 +30083,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -30135,17 +30147,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -30188,6 +30200,326 @@
         "test"
       ]
     },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian10-GCC-x86-Debug-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 21600000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 21600000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian10-GCC-x86-Release-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian10-GCC-x86_64-Debug-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian10-GCC-x86_64-Release-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia",
+        "docker_installed:true"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
     "Test-Debian9-Clang-GCE-CPU-AVX2-x86-Debug-All": {
       "caches": [
         {
@@ -30199,27 +30531,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30277,27 +30609,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30355,27 +30687,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30438,27 +30770,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30516,27 +30848,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30599,27 +30931,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30677,27 +31009,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30755,27 +31087,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30833,27 +31165,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30911,27 +31243,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -30989,27 +31321,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31067,27 +31399,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31150,27 +31482,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31228,27 +31560,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31306,27 +31638,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31384,27 +31716,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31462,27 +31794,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31540,27 +31872,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31607,6 +31939,84 @@
         "test"
       ]
     },
+    "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-x86_64-Release-SK_USE_SKVM_BLITTER"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "machine_type:n1-standard-16",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
     "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN": {
       "caches": [
         {
@@ -31618,27 +32028,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31701,27 +32111,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31778,27 +32188,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31855,27 +32265,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -31933,27 +32343,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32016,27 +32426,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32094,27 +32504,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32176,27 +32586,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32263,27 +32673,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32345,27 +32755,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32432,27 +32842,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32494,7 +32904,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "swarm_recipe.isolate",
+      "isolate": "skqp.isolate",
       "max_attempts": 2,
       "outputs": [
         "test"
@@ -32511,27 +32921,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32593,27 +33003,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32675,27 +33085,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32762,27 +33172,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32844,27 +33254,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -32931,27 +33341,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33023,27 +33433,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33110,27 +33520,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33202,27 +33612,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33289,27 +33699,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33371,27 +33781,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33458,27 +33868,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33550,27 +33960,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33637,27 +34047,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33729,27 +34139,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33816,27 +34226,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33903,27 +34313,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -33985,27 +34395,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -34067,27 +34477,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -34149,27 +34559,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -34231,17 +34641,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -34277,7 +34687,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "swarm_recipe.isolate",
+      "isolate": "pathkit.isolate",
       "max_attempts": 2,
       "outputs": [
         "test"
@@ -34294,17 +34704,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -34340,7 +34750,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "swarm_recipe.isolate",
+      "isolate": "canvaskit.isolate",
       "max_attempts": 2,
       "outputs": [
         "test"
@@ -34357,17 +34767,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -34403,7 +34813,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "swarm_recipe.isolate",
+      "isolate": "pathkit.isolate",
       "max_attempts": 2,
       "outputs": [
         "test"
@@ -34420,17 +34830,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -34468,319 +34878,7 @@
         "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
       },
       "io_timeout_ns": 14400000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86-Debug"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "machine_type:n1-standard-16",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 21600000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 21600000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86-Release"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "machine_type:n1-standard-16",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Debug"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "machine_type:n1-standard-16",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Release"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "machine_type:n1-standard-16",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
+      "isolate": "canvaskit.isolate",
       "max_attempts": 2,
       "outputs": [
         "test"
@@ -34797,27 +34895,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -34874,27 +34972,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -34951,27 +35049,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35028,27 +35126,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35105,27 +35203,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35182,27 +35280,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35259,27 +35357,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35336,27 +35434,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35413,27 +35511,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35490,27 +35588,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35567,27 +35665,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35644,27 +35742,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35721,27 +35819,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35798,27 +35896,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35875,27 +35973,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -35952,27 +36050,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36029,27 +36127,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36106,27 +36204,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36183,27 +36281,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36260,27 +36358,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36337,27 +36435,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36414,27 +36512,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36491,27 +36589,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36568,27 +36666,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36645,27 +36743,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36722,27 +36820,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36800,27 +36898,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36878,27 +36976,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -36956,27 +37054,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37034,27 +37132,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37100,391 +37198,6 @@
         "test"
       ]
     },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Mac-Clang-x86_64-Debug"
-      ],
-      "dimensions": [
-        "gpu:8086:1626",
-        "os:Mac-10.14.3",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Mac-Clang-x86_64-Debug-CommandBuffer"
-      ],
-      "dimensions": [
-        "gpu:8086:1626",
-        "os:Mac-10.14.3",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Mac-Clang-x86_64-Debug-Metal"
-      ],
-      "dimensions": [
-        "gpu:8086:1626",
-        "os:Mac-10.14.3",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Mac-Clang-x86_64-Release"
-      ],
-      "dimensions": [
-        "gpu:8086:1626",
-        "os:Mac-10.14.3",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Mac-Clang-x86_64-Release-Metal"
-      ],
-      "dimensions": [
-        "gpu:8086:1626",
-        "os:Mac-10.14.3",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 14400000000000,
-      "expiration_ns": 72000000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 14400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 2,
-      "outputs": [
-        "test"
-      ]
-    },
     "Test-Mac10.14-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts": {
       "caches": [
         {
@@ -37496,27 +37209,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37562,6 +37275,468 @@
         "test"
       ]
     },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Debug"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Debug-CommandBuffer"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Debug-Metal"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Debug"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Release"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Mac-Clang-x86_64-Release-Metal"
+      ],
+      "dimensions": [
+        "gpu:8086:1626",
+        "os:Mac-10.15.1",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 14400000000000,
+      "expiration_ns": 72000000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 14400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 2,
+      "outputs": [
+        "test"
+      ]
+    },
     "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All": {
       "caches": [
         {
@@ -37573,27 +37748,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37650,27 +37825,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37732,27 +37907,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37809,27 +37984,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37891,27 +38066,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -37968,27 +38143,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38050,27 +38225,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38132,27 +38307,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38214,27 +38389,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38291,27 +38466,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38373,27 +38548,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38455,27 +38630,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38537,27 +38712,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38624,27 +38799,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38690,6 +38865,255 @@
         "test"
       ]
     },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ],
+      "dimensions": [
+        "gpu:10de:1cb3-430.14",
+        "os:Ubuntu-18.04",
+        "pool:Skia",
+        "valgrind:1"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 32400000000000,
+      "expiration_ns": 172800000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 32400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 1,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ],
+      "dimensions": [
+        "gpu:10de:1cb3-430.14",
+        "os:Ubuntu-18.04",
+        "pool:Skia",
+        "valgrind:1"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 32400000000000,
+      "expiration_ns": 172800000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 32400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 1,
+      "outputs": [
+        "test"
+      ]
+    },
+    "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "skia/bots/skimage",
+          "path": "skimage",
+          "version": "version:40"
+        },
+        {
+          "name": "skia/bots/skp",
+          "path": "skp",
+          "version": "version:215"
+        },
+        {
+          "name": "skia/bots/svg",
+          "path": "svg",
+          "version": "version:9"
+        },
+        {
+          "name": "skia/bots/valgrind",
+          "path": "valgrind",
+          "version": "version:9"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "test",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Build-Debian9-Clang-x86_64-Release-SK_CPU_LIMIT_SSE41"
+      ],
+      "dimensions": [
+        "gpu:10de:1cb3-430.14",
+        "os:Ubuntu-18.04",
+        "pool:Skia",
+        "valgrind:1"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 32400000000000,
+      "expiration_ns": 172800000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 32400000000000,
+      "isolate": "test_skia_bundled.isolate",
+      "max_attempts": 1,
+      "outputs": [
+        "test"
+      ]
+    },
     "Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan": {
       "caches": [
         {
@@ -38701,27 +39125,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -38772,255 +39196,6 @@
         "test"
       ]
     },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        },
-        {
-          "name": "skia/bots/valgrind",
-          "path": "valgrind",
-          "version": "version:7"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41"
-      ],
-      "dimensions": [
-        "gpu:10de:1cb3-430.14",
-        "os:Ubuntu-18.04",
-        "pool:Skia",
-        "valgrind:1"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 32400000000000,
-      "expiration_ns": 172800000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 32400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 1,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        },
-        {
-          "name": "skia/bots/valgrind",
-          "path": "valgrind",
-          "version": "version:7"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_PreAbandonGpuContext_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41"
-      ],
-      "dimensions": [
-        "gpu:10de:1cb3-430.14",
-        "os:Ubuntu-18.04",
-        "pool:Skia",
-        "valgrind:1"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 32400000000000,
-      "expiration_ns": 172800000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 32400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 1,
-      "outputs": [
-        "test"
-      ]
-    },
-    "Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "skia/bots/skimage",
-          "path": "skimage",
-          "version": "version:39"
-        },
-        {
-          "name": "skia/bots/skp",
-          "path": "skp",
-          "version": "version:210"
-        },
-        {
-          "name": "skia/bots/svg",
-          "path": "svg",
-          "version": "version:9"
-        },
-        {
-          "name": "skia/bots/valgrind",
-          "path": "valgrind",
-          "version": "version:7"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "test",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Ubuntu18-GCC-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41\",\"gold_hashes_url\":\"https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Build-Debian9-GCC-x86_64-Release-SK_CPU_LIMIT_SSE41"
-      ],
-      "dimensions": [
-        "gpu:10de:1cb3-430.14",
-        "os:Ubuntu-18.04",
-        "pool:Skia",
-        "valgrind:1"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 32400000000000,
-      "expiration_ns": 172800000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 32400000000000,
-      "isolate": "test_skia_bundled.isolate",
-      "max_attempts": 1,
-      "outputs": [
-        "test"
-      ]
-    },
     "Test-Win10-Clang-AlphaR2-GPU-RadeonR9M470X-x86_64-Debug-All": {
       "caches": [
         {
@@ -39032,17 +39207,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39052,12 +39227,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39114,17 +39289,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39134,12 +39309,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39196,17 +39371,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39216,12 +39391,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39278,17 +39453,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39298,12 +39473,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39360,17 +39535,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39380,12 +39555,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39442,17 +39617,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39462,12 +39637,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39524,17 +39699,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39544,12 +39719,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39606,17 +39781,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39626,12 +39801,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39688,17 +39863,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39708,12 +39883,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39770,17 +39945,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39790,12 +39965,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39852,17 +40027,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39872,12 +40047,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -39934,17 +40109,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -39954,12 +40129,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40016,17 +40191,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40036,12 +40211,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40098,17 +40273,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40118,12 +40293,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40180,17 +40355,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40200,12 +40375,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40262,17 +40437,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40282,12 +40457,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40349,17 +40524,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40369,12 +40544,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40431,17 +40606,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40451,12 +40626,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40513,17 +40688,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40533,12 +40708,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40595,17 +40770,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40615,12 +40790,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40677,17 +40852,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40697,12 +40872,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40759,17 +40934,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40779,12 +40954,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40846,17 +41021,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40866,12 +41041,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -40928,17 +41103,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -40948,12 +41123,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41010,17 +41185,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41030,12 +41205,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41092,17 +41267,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41112,12 +41287,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41174,17 +41349,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41194,12 +41369,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41256,17 +41431,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41276,12 +41451,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41338,17 +41513,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41358,12 +41533,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41420,17 +41595,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41440,12 +41615,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41502,17 +41677,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41522,12 +41697,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41584,17 +41759,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41604,12 +41779,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41666,17 +41841,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41686,12 +41861,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41748,17 +41923,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41768,12 +41943,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41830,17 +42005,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41850,12 +42025,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41912,17 +42087,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -41932,12 +42107,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -41994,17 +42169,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42014,12 +42189,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42076,17 +42251,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42096,12 +42271,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42158,17 +42333,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42178,12 +42353,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42240,17 +42415,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42260,12 +42435,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42322,17 +42497,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42342,12 +42517,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42404,17 +42579,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42424,12 +42599,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42486,17 +42661,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42506,12 +42681,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42568,17 +42743,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42588,12 +42763,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42650,17 +42825,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42670,12 +42845,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42732,17 +42907,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42752,12 +42927,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42814,17 +42989,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42834,12 +43009,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42896,17 +43071,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42916,12 +43091,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -42978,17 +43153,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -42998,12 +43173,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43060,17 +43235,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43080,12 +43255,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43142,17 +43317,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43162,12 +43337,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43224,17 +43399,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43244,12 +43419,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43306,17 +43481,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43326,12 +43501,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43388,17 +43563,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43408,12 +43583,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43470,17 +43645,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43490,12 +43665,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43552,17 +43727,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43572,12 +43747,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43634,17 +43809,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43654,12 +43829,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43716,17 +43891,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43736,12 +43911,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43798,17 +43973,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43818,12 +43993,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43880,17 +44055,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43900,12 +44075,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -43962,17 +44137,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -43982,12 +44157,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44044,17 +44219,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44064,12 +44239,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44126,17 +44301,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44146,12 +44321,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44208,17 +44383,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44228,12 +44403,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44290,17 +44465,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44310,12 +44485,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44372,17 +44547,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44392,12 +44567,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44454,17 +44629,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44474,12 +44649,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44536,17 +44711,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44556,12 +44731,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44618,27 +44793,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44695,27 +44870,27 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44772,17 +44947,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44792,12 +44967,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44856,17 +45031,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44876,12 +45051,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -44940,17 +45115,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -44960,12 +45135,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45024,17 +45199,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45044,12 +45219,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45108,17 +45283,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45128,12 +45303,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45192,17 +45367,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45212,12 +45387,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45276,17 +45451,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45296,12 +45471,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45360,17 +45535,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45380,12 +45555,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45444,17 +45619,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45464,12 +45639,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45528,17 +45703,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45548,12 +45723,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45612,17 +45787,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45632,12 +45807,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45696,17 +45871,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45716,12 +45891,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45780,17 +45955,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45800,12 +45975,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45864,17 +46039,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45884,12 +46059,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -45948,17 +46123,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -45968,12 +46143,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46030,17 +46205,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46050,12 +46225,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46112,17 +46287,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46132,12 +46307,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46194,17 +46369,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46214,12 +46389,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46276,17 +46451,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46296,12 +46471,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46358,17 +46533,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46378,12 +46553,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46440,17 +46615,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46460,12 +46635,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46522,17 +46697,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46542,12 +46717,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46604,17 +46779,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46624,12 +46799,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46686,17 +46861,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -46706,12 +46881,12 @@
         {
           "name": "skia/bots/skimage",
           "path": "skimage",
-          "version": "version:39"
+          "version": "version:40"
         },
         {
           "name": "skia/bots/skp",
           "path": "skp",
-          "version": "version:210"
+          "version": "version:215"
         },
         {
           "name": "skia/bots/svg",
@@ -46768,17 +46943,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -46833,17 +47008,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -46898,17 +47073,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -46963,17 +47138,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47028,17 +47203,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47093,17 +47268,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47158,17 +47333,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47223,17 +47398,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47288,17 +47463,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47353,17 +47528,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47418,17 +47593,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         }
       ],
       "command": [
@@ -47483,22 +47658,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -47549,22 +47724,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -47615,22 +47790,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -47681,22 +47856,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -47747,22 +47922,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -47802,138 +47977,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_calmbench_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All\",\"gs_bucket\":\"skia-calmbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Calmbench-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-calmbench-upload@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_calmbench_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All\",\"gs_bucket\":\"skia-calmbench\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Calmbench-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-calmbench-upload@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android": {
       "caches": [
         {
@@ -47945,22 +47988,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48011,22 +48054,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48077,22 +48120,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48143,22 +48186,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48209,22 +48252,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48275,22 +48318,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48341,22 +48384,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48407,22 +48450,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48473,22 +48516,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48539,22 +48582,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48605,22 +48648,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48671,22 +48714,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48737,22 +48780,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48803,22 +48846,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48869,22 +48912,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -48935,22 +48978,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49001,22 +49044,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49067,22 +49110,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49122,6 +49165,72 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Upload-Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_nano_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Perf-Android-Clang-Nexus5x-CPU-Snapdragon808-arm64-Release-All-Android_Wuffs"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Upload-Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android": {
       "caches": [
         {
@@ -49133,22 +49242,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49199,22 +49308,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49265,17 +49374,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -49285,7 +49394,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49336,17 +49445,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -49356,7 +49465,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49407,22 +49516,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49473,22 +49582,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49539,22 +49648,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49605,22 +49714,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49671,22 +49780,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49737,22 +49846,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49803,22 +49912,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -49858,138 +49967,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_CCPR_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -50001,22 +49978,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50056,72 +50033,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Release-All-Android_Vulkan_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android": {
       "caches": [
         {
@@ -50133,22 +50044,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50188,138 +50099,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_CCPR_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -50331,22 +50110,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50386,72 +50165,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3-GPU-Adreno630-arm64-Release-All-Android_Vulkan_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android": {
       "caches": [
         {
@@ -50463,22 +50176,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50518,138 +50231,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_CCPR_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Skpbench"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan": {
       "caches": [
         {
@@ -50661,22 +50242,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50716,7 +50297,7 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench": {
+    "Upload-Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
       "caches": [
         {
           "name": "vpython",
@@ -50727,22 +50308,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50750,12 +50331,78 @@
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
         "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
         "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Release-All-Android_Vulkan_Skpbench"
+        "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_nano_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Perf-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan"
       ],
       "dimensions": [
         "cpu:x86-64-Haswell_GCE",
@@ -50793,22 +50440,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50859,22 +50506,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50925,22 +50572,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -50991,22 +50638,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51057,22 +50704,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51123,22 +50770,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51189,22 +50836,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51255,22 +50902,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51321,22 +50968,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51387,22 +51034,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51453,22 +51100,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51519,22 +51166,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51585,22 +51232,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51651,22 +51298,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51717,22 +51364,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51783,22 +51430,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51849,22 +51496,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51915,22 +51562,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -51981,22 +51628,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52047,22 +51694,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52113,22 +51760,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52179,22 +51826,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52245,22 +51892,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52311,22 +51958,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52377,22 +52024,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52443,22 +52090,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52509,22 +52156,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52575,22 +52222,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52641,22 +52288,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52707,22 +52354,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52773,22 +52420,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52839,22 +52486,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52905,22 +52552,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -52971,22 +52618,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53037,22 +52684,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53103,22 +52750,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53169,22 +52816,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53235,22 +52882,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53290,7 +52937,7 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+    "Upload-Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
       "caches": [
         {
           "name": "vpython",
@@ -53301,22 +52948,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53324,12 +52971,12 @@
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
         "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
         "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
+        "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
       ],
       "dimensions": [
         "cpu:x86-64-Haswell_GCE",
@@ -53356,7 +53003,7 @@
       "max_attempts": 2,
       "service_account": "skia-external-nano-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
+    "Upload-Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer": {
       "caches": [
         {
           "name": "vpython",
@@ -53367,22 +53014,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53390,12 +53037,12 @@
         "skia/infra/bots/run_recipe.py",
         "${ISOLATED_OUTDIR}",
         "upload_nano_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer\",\"gs_bucket\":\"skia-perf\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
         "skia"
       ],
       "dependencies": [
         "Housekeeper-PerCommit-BundleRecipes",
-        "Perf-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer"
+        "Perf-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-CommandBuffer"
       ],
       "dimensions": [
         "cpu:x86-64-Haswell_GCE",
@@ -53433,22 +53080,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53499,22 +53146,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53565,22 +53212,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53631,22 +53278,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53697,17 +53344,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -53717,7 +53364,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53768,17 +53415,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -53788,7 +53435,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53839,17 +53486,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -53859,7 +53506,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53910,17 +53557,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -53930,7 +53577,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -53981,17 +53628,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54001,7 +53648,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54052,17 +53699,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54072,7 +53719,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54123,17 +53770,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54143,7 +53790,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54194,17 +53841,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54214,7 +53861,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54265,17 +53912,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54285,7 +53932,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54336,17 +53983,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54356,7 +54003,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54407,17 +54054,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54427,7 +54074,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54478,17 +54125,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54498,7 +54145,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54549,17 +54196,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54569,7 +54216,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54620,17 +54267,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54640,7 +54287,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54691,17 +54338,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54711,7 +54358,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54762,17 +54409,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54782,7 +54429,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54833,17 +54480,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54853,7 +54500,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54904,17 +54551,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54924,7 +54571,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -54975,17 +54622,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -54995,7 +54642,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55046,17 +54693,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55066,7 +54713,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55117,17 +54764,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55137,7 +54784,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55188,17 +54835,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55208,7 +54855,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55259,17 +54906,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55279,7 +54926,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55330,17 +54977,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55350,7 +54997,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55401,17 +55048,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55421,7 +55068,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55472,17 +55119,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55492,7 +55139,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55543,17 +55190,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55563,7 +55210,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55614,17 +55261,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55634,7 +55281,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55685,17 +55332,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55705,7 +55352,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55756,17 +55403,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55776,7 +55423,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55827,17 +55474,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55847,7 +55494,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55898,17 +55545,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55918,7 +55565,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -55969,17 +55616,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -55989,7 +55636,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56040,22 +55687,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56106,22 +55753,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56172,22 +55819,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56238,22 +55885,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56304,22 +55951,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56370,22 +56017,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56436,22 +56083,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56502,22 +56149,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56568,22 +56215,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56634,22 +56281,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56700,22 +56347,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56766,22 +56413,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56832,22 +56479,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56898,22 +56545,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -56964,22 +56611,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57030,22 +56677,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57096,22 +56743,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57162,22 +56809,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57228,22 +56875,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57294,22 +56941,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57360,22 +57007,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57426,22 +57073,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57492,22 +57139,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57558,22 +57205,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57624,22 +57271,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57690,22 +57337,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57756,22 +57403,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57822,22 +57469,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57888,22 +57535,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -57954,22 +57601,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58020,22 +57667,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58086,22 +57733,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58152,22 +57799,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58218,22 +57865,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58284,22 +57931,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58350,22 +57997,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58416,22 +58063,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58482,22 +58129,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58548,22 +58195,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58614,22 +58261,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58680,22 +58327,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58746,22 +58393,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58812,22 +58459,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58878,22 +58525,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -58944,22 +58591,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59010,22 +58657,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59076,22 +58723,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59142,22 +58789,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59208,17 +58855,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59228,7 +58875,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59279,17 +58926,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59299,7 +58946,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59350,17 +58997,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59370,7 +59017,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59421,17 +59068,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59441,7 +59088,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59492,17 +59139,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59512,7 +59159,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59563,17 +59210,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -59583,7 +59230,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59634,22 +59281,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59700,22 +59347,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59766,22 +59413,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59832,22 +59479,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59898,22 +59545,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -59964,22 +59611,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60030,22 +59677,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60096,22 +59743,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60162,22 +59809,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60228,22 +59875,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60294,22 +59941,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60360,22 +60007,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60426,22 +60073,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60492,22 +60139,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60558,22 +60205,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60624,22 +60271,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60690,22 +60337,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60756,22 +60403,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60822,22 +60469,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60888,22 +60535,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -60954,22 +60601,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61009,6 +60656,270 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Debug-All-Android_Vulkan"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Android-Clang-Pixel4-GPU-Adreno640-arm64-Release-All-Android_Vulkan"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Upload-Test-Android-Clang-TecnoSpark3Pro-GPU-PowerVRGE8320-arm-Debug-All-Android": {
       "caches": [
         {
@@ -61020,22 +60931,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61086,22 +60997,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61152,22 +61063,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61218,22 +61129,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61284,22 +61195,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61350,22 +61261,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61416,22 +61327,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61482,22 +61393,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61548,22 +61459,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61614,22 +61525,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61680,22 +61591,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61746,22 +61657,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61812,22 +61723,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61878,22 +61789,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -61944,22 +61855,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62010,22 +61921,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62076,22 +61987,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62142,22 +62053,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62197,6 +62108,270 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Debian10-GCC-GCE-CPU-AVX2-x86-Release-All-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Release-All-Docker"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86-Debug-All": {
       "caches": [
         {
@@ -62208,22 +62383,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62274,22 +62449,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62340,22 +62515,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62406,22 +62581,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62472,22 +62647,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62538,22 +62713,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62604,22 +62779,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62670,22 +62845,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62736,22 +62911,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62802,22 +62977,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62868,22 +63043,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -62934,22 +63109,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63000,22 +63175,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63066,22 +63241,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63132,22 +63307,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63187,6 +63362,72 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SK_USE_SKVM_BLITTER"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Upload-Test-Debian9-Clang-GCE-CPU-AVX512-x86_64-Debug-All": {
       "caches": [
         {
@@ -63198,22 +63439,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63264,22 +63505,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63330,22 +63571,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63396,22 +63637,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63462,22 +63703,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63528,22 +63769,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63594,22 +63835,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63660,22 +63901,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63726,22 +63967,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63792,22 +64033,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63858,22 +64099,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63924,22 +64165,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -63990,22 +64231,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64056,22 +64297,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64122,22 +64363,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64188,22 +64429,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64254,22 +64495,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64320,22 +64561,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64386,22 +64627,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64452,22 +64693,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64518,22 +64759,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64584,22 +64825,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64650,22 +64891,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64716,22 +64957,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -64771,270 +65012,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Debug-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Debian9-GCC-GCE-CPU-AVX2-x86-Release-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Debug-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Debian9-GCC-GCE-CPU-AVX2-x86_64-Release-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Test-Mac10.13-Clang-MacBook10.1-GPU-IntelHD615-x86_64-Debug-All": {
       "caches": [
         {
@@ -65046,22 +65023,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65112,22 +65089,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65178,22 +65155,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65244,22 +65221,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65310,22 +65287,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65376,22 +65353,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65442,22 +65419,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65508,22 +65485,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65574,22 +65551,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65640,22 +65617,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65706,22 +65683,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65772,22 +65749,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65838,22 +65815,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65904,22 +65881,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -65970,22 +65947,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66036,22 +66013,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66102,22 +66079,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66168,22 +66145,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66234,22 +66211,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66300,22 +66277,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66366,22 +66343,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66432,22 +66409,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66498,22 +66475,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66553,336 +66530,6 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
-    "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
-    "Upload-Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
-      "caches": [
-        {
-          "name": "vpython",
-          "path": "cache/vpython"
-        }
-      ],
-      "cipd_packages": [
-        {
-          "name": "infra/tools/luci/kitchen/${platform}",
-          "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
-        },
-        {
-          "name": "infra/tools/luci-auth/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
-        },
-        {
-          "name": "infra/tools/luci/vpython/${platform}",
-          "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
-        },
-        {
-          "name": "infra/gsutil",
-          "path": "cipd_bin_packages",
-          "version": "version:4.28"
-        }
-      ],
-      "command": [
-        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
-        "skia/infra/bots/run_recipe.py",
-        "${ISOLATED_OUTDIR}",
-        "upload_dm_results",
-        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
-        "skia"
-      ],
-      "dependencies": [
-        "Housekeeper-PerCommit-BundleRecipes",
-        "Test-Mac10.14-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal"
-      ],
-      "dimensions": [
-        "cpu:x86-64-Haswell_GCE",
-        "gpu:none",
-        "machine_type:n1-highmem-2",
-        "os:Debian-9.8",
-        "pool:Skia"
-      ],
-      "env_prefixes": {
-        "PATH": [
-          "cipd_bin_packages",
-          "cipd_bin_packages/bin"
-        ],
-        "VPYTHON_VIRTUALENV_ROOT": [
-          "cache/vpython"
-        ]
-      },
-      "execution_timeout_ns": 3600000000000,
-      "extra_tags": {
-        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
-      },
-      "io_timeout_ns": 3600000000000,
-      "isolate": "swarm_recipe.isolate",
-      "max_attempts": 2,
-      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
-    },
     "Upload-Test-Mac10.14-Clang-VMware7.1-CPU-AVX-x86_64-Debug-All-NativeFonts": {
       "caches": [
         {
@@ -66894,22 +66541,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -66949,6 +66596,402 @@
       "max_attempts": 2,
       "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
     },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-CommandBuffer"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-Metal"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Debug-All-NativeFonts"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
+    "Upload-Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal": {
+      "caches": [
+        {
+          "name": "vpython",
+          "path": "cache/vpython"
+        }
+      ],
+      "cipd_packages": [
+        {
+          "name": "infra/tools/luci/kitchen/${platform}",
+          "path": ".",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci-auth/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/tools/luci/vpython/${platform}",
+          "path": "cipd_bin_packages",
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
+        },
+        {
+          "name": "infra/gsutil",
+          "path": "cipd_bin_packages",
+          "version": "version:4.46"
+        }
+      ],
+      "command": [
+        "cipd_bin_packages/vpython${EXECUTABLE_SUFFIX}",
+        "skia/infra/bots/run_recipe.py",
+        "${ISOLATED_OUTDIR}",
+        "upload_dm_results",
+        "{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE_INT)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET_INT)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\",\"task_id\":\"<(TASK_ID)\"}",
+        "skia"
+      ],
+      "dependencies": [
+        "Housekeeper-PerCommit-BundleRecipes",
+        "Test-Mac10.15-Clang-MacBookAir7.2-GPU-IntelHD6000-x86_64-Release-All-Metal"
+      ],
+      "dimensions": [
+        "cpu:x86-64-Haswell_GCE",
+        "gpu:none",
+        "machine_type:n1-highmem-2",
+        "os:Debian-9.8",
+        "pool:Skia"
+      ],
+      "env_prefixes": {
+        "PATH": [
+          "cipd_bin_packages",
+          "cipd_bin_packages/bin"
+        ],
+        "VPYTHON_VIRTUALENV_ROOT": [
+          "cache/vpython"
+        ]
+      },
+      "execution_timeout_ns": 3600000000000,
+      "extra_tags": {
+        "log_location": "logdog://logs.chromium.org/skia/${SWARMING_TASK_ID}/+/annotations"
+      },
+      "io_timeout_ns": 3600000000000,
+      "isolate": "swarm_recipe.isolate",
+      "max_attempts": 2,
+      "service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
+    },
     "Upload-Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Debug-All": {
       "caches": [
         {
@@ -66960,22 +67003,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67026,22 +67069,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67092,22 +67135,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67158,22 +67201,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67224,22 +67267,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67290,22 +67333,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67356,22 +67399,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67422,22 +67465,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67488,17 +67531,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67508,7 +67551,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67559,17 +67602,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67579,7 +67622,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67630,17 +67673,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67650,7 +67693,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67701,17 +67744,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67721,7 +67764,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67772,17 +67815,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67792,7 +67835,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67843,17 +67886,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67863,7 +67906,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67914,17 +67957,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -67934,7 +67977,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -67985,17 +68028,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68005,7 +68048,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68056,17 +68099,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68076,7 +68119,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68127,17 +68170,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68147,7 +68190,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68198,17 +68241,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68218,7 +68261,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68269,17 +68312,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68289,7 +68332,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68340,17 +68383,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68360,7 +68403,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68411,17 +68454,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68431,7 +68474,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68482,17 +68525,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68502,7 +68545,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68553,17 +68596,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68573,7 +68616,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68624,17 +68667,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68644,7 +68687,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68695,17 +68738,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68715,7 +68758,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68766,17 +68809,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68786,7 +68829,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68837,17 +68880,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68857,7 +68900,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68908,17 +68951,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68928,7 +68971,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -68979,17 +69022,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -68999,7 +69042,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69050,17 +69093,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69070,7 +69113,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69121,17 +69164,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69141,7 +69184,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69192,17 +69235,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69212,7 +69255,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69263,17 +69306,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69283,7 +69326,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69334,17 +69377,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69354,7 +69397,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69405,17 +69448,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69425,7 +69468,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69476,17 +69519,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69496,7 +69539,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69547,17 +69590,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69567,7 +69610,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69618,17 +69661,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69638,7 +69681,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69689,17 +69732,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69709,7 +69752,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69760,17 +69803,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69780,7 +69823,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69831,17 +69874,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69851,7 +69894,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69902,17 +69945,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69922,7 +69965,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -69973,17 +70016,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -69993,7 +70036,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70044,17 +70087,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70064,7 +70107,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70115,17 +70158,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70135,7 +70178,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70186,17 +70229,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70206,7 +70249,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70257,17 +70300,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70277,7 +70320,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70328,17 +70371,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70348,7 +70391,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70399,17 +70442,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70419,7 +70462,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70470,17 +70513,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70490,7 +70533,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70541,17 +70584,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70561,7 +70604,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70612,17 +70655,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70632,7 +70675,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70683,17 +70726,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70703,7 +70746,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70754,17 +70797,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70774,7 +70817,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70825,17 +70868,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70845,7 +70888,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70896,17 +70939,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70916,7 +70959,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -70967,17 +71010,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -70987,7 +71030,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71038,17 +71081,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71058,7 +71101,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71109,17 +71152,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71129,7 +71172,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71180,17 +71223,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71200,7 +71243,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71251,17 +71294,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71271,7 +71314,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71322,17 +71365,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71342,7 +71385,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71393,17 +71436,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71413,7 +71456,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71464,17 +71507,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71484,7 +71527,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71535,17 +71578,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71555,7 +71598,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71606,17 +71649,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71626,7 +71669,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71677,17 +71720,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71697,7 +71740,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71748,17 +71791,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71768,7 +71811,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71819,17 +71862,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71839,7 +71882,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71890,17 +71933,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71910,7 +71953,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -71961,17 +72004,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -71981,7 +72024,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72032,17 +72075,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72052,7 +72095,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72103,22 +72146,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72169,22 +72212,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72235,17 +72278,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72255,7 +72298,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72306,17 +72349,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72326,7 +72369,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72377,17 +72420,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72397,7 +72440,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72448,17 +72491,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72468,7 +72511,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72519,17 +72562,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72539,7 +72582,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72590,17 +72633,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72610,7 +72653,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72661,17 +72704,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72681,7 +72724,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72732,17 +72775,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72752,7 +72795,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72803,17 +72846,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72823,7 +72866,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72874,17 +72917,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72894,7 +72937,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -72945,17 +72988,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -72965,7 +73008,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73016,17 +73059,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73036,7 +73079,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73087,17 +73130,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73107,7 +73150,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73158,17 +73201,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73178,7 +73221,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73229,17 +73272,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73249,7 +73292,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73300,17 +73343,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73320,7 +73363,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73371,17 +73414,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73391,7 +73434,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73442,17 +73485,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73462,7 +73505,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73513,17 +73556,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73533,7 +73576,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73584,17 +73627,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73604,7 +73647,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73655,17 +73698,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73675,7 +73718,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73726,17 +73769,17 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/python/cpython/${platform}",
@@ -73746,7 +73789,7 @@
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73797,22 +73840,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73863,22 +73906,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73929,22 +73972,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -73995,22 +74038,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74061,22 +74104,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74127,22 +74170,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74193,22 +74236,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74259,22 +74302,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74325,22 +74368,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74391,22 +74434,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
@@ -74457,22 +74500,22 @@
         {
           "name": "infra/tools/luci/kitchen/${platform}",
           "path": ".",
-          "version": "git_revision:d8f38ca9494b5af249942631f9cee45927f6b4bc"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci-auth/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:2c805f1c716f6c5ad2126b27ec88b8585a09481e"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/tools/luci/vpython/${platform}",
           "path": "cipd_bin_packages",
-          "version": "git_revision:f96db4b66034c859090be3c47eb38227277f228b"
+          "version": "git_revision:0e171233385f11fd2b7458728c8ee439d4db53f1"
         },
         {
           "name": "infra/gsutil",
           "path": "cipd_bin_packages",
-          "version": "version:4.28"
+          "version": "version:4.46"
         }
       ],
       "command": [
diff --git a/infra/bots/test_utils.py b/infra/bots/test_utils.py
old mode 100644
new mode 100755
diff --git a/infra/bots/update_meta_config.py b/infra/bots/update_meta_config.py
index 8688e78..c470e12 100644
--- a/infra/bots/update_meta_config.py
+++ b/infra/bots/update_meta_config.py
@@ -17,53 +17,14 @@
 
 SKIA_REPO_TEMPLATE = 'https://skia.googlesource.com/%s.git'
 
-CQ_INCLUDE_CHROMIUM_TRYBOTS = [
-    ('luci.chromium.try', [
-        'android_optional_gpu_tests_rel',
-        'linux-blink-rel',
-        'linux_chromium_compile_dbg_ng',
-        'linux_chromium_dbg_ng',
-        'linux_chromium_rel_ng',
-        'linux_optional_gpu_tests_rel',
-        'mac10.10-blink-rel',
-        'mac10.11-blink-rel',
-        'mac10.12-blink-rel',
-        'mac10.13-blink-rel',
-        'mac10.13_retina-blink-rel',
-        'mac_chromium_compile_dbg_ng',
-        'mac_chromium_compile_rel_ng',
-        'mac_chromium_dbg_ng',
-        'mac_chromium_rel_ng',
-        'mac_optional_gpu_tests_rel',
-        'win10-blink-rel',
-        'win7-blink-rel',
-        'win_chromium_compile_dbg_ng',
-        'win_chromium_dbg_ng',
-        'win_optional_gpu_tests_rel',
-    ]),
-    ('master.tryserver.chromium.linux', [
-        'linux_chromium_compile_rel_ng',
-    ]),
-    ('master.tryserver.chromium.win', [
-        'win_chromium_compile_rel_ng',
-        'win7_chromium_rel_ng',
-        'win10_chromium_x64_rel_ng',
-    ]),
-    ('master.tryserver.chromium.android', [
-        'android_blink_rel',
-        'android_compile_dbg',
-        'android_compile_rel',
-        'android_n5x_swarming_dbg',
-        'android_n5x_swarming_rel',
-    ])
+CQ_INCLUDE_CHROMIUM_BUCKETS = [
+    'luci.chromium.try',
 ]
 
 
-def addChromiumTrybots(f):
-  for master, bots in CQ_INCLUDE_CHROMIUM_TRYBOTS:
-    f.write('[bucket "%s"]\n' % master)
-    for bot in bots:
-      f.write('\tbuilder = %s\n' % bot)
+def addChromiumBuckets(f):
+  for bucket in CQ_INCLUDE_CHROMIUM_BUCKETS:
+    f.write('[bucket "%s"]\n' % bucket)
 
 
 def main():
@@ -92,7 +53,7 @@
     with open(buildbucket_config, 'w') as f:
 
       if args.repo_name == 'skia':
-        addChromiumTrybots(f)
+        addChromiumBuckets(f)
 
       # Adding all Skia jobs.
       f.write('[bucket "skia.primary"]\n')
diff --git a/infra/bots/upload_skps.py b/infra/bots/upload_skps.py
index c5b7ac30..8d9fd67 100644
--- a/infra/bots/upload_skps.py
+++ b/infra/bots/upload_skps.py
@@ -12,6 +12,7 @@
 import urllib2
 
 import git_utils
+import utils
 
 
 COMMIT_MSG = '''Update SKP version
@@ -21,7 +22,6 @@
 TBR=rmistry@google.com
 NO_MERGE_BUILDS
 '''
-SKIA_REPO = 'https://skia.googlesource.com/skia.git'
 
 
 def main(target_dir):
@@ -32,7 +32,7 @@
   skp_dir = os.path.join(infrabots_dir, 'assets', 'skp')
   upload_py = os.path.join(skp_dir, 'upload.py')
 
-  with git_utils.NewGitCheckout(repository=SKIA_REPO):
+  with git_utils.NewGitCheckout(repository=utils.SKIA_REPO):
     # First verify that there are no gen_tasks diffs.
     tmp_infrabots_dir = os.path.join(os.getcwd(), 'infra', 'bots')
     tmp_gen_tasks = os.path.join(tmp_infrabots_dir, 'gen_tasks.go')
diff --git a/infra/bots/utils.py b/infra/bots/utils.py
old mode 100644
new mode 100755
index 8b08aec..42c1ced
--- a/infra/bots/utils.py
+++ b/infra/bots/utils.py
@@ -17,6 +17,8 @@
 import uuid
 
 
+SKIA_REPO = 'https://skia.googlesource.com/skia.git'
+
 GCLIENT = 'gclient.bat' if sys.platform == 'win32' else 'gclient'
 WHICH = 'where' if sys.platform == 'win32' else 'which'
 GIT = subprocess.check_output([WHICH, 'git']).splitlines()[0]
diff --git a/infra/bots/zip_utils.py b/infra/bots/zip_utils.py
old mode 100644
new mode 100755
diff --git a/infra/bots/zip_utils_test.py b/infra/bots/zip_utils_test.py
old mode 100644
new mode 100755
diff --git a/infra/cmake/build_skia.sh b/infra/cmake/build_skia.sh
index dbd50dc..2d36d9e 100755
--- a/infra/cmake/build_skia.sh
+++ b/infra/cmake/build_skia.sh
@@ -27,6 +27,8 @@
 gn gen out/CMAKE --args='is_debug=false' --ide=json --json-ide-script=../../gn/gn_to_cmake.py
 
 cd $SKIA_DIR/out/CMAKE
+export CC=/usr/local/bin/clang
+export CXX=/usr/local/bin/clang++
 cmake -G"CodeBlocks - Unix Makefiles" .
 cmake --build . --parallel 8
 
diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg
index b4df7b8..61f28c9 100644
--- a/infra/config/recipes.cfg
+++ b/infra/config/recipes.cfg
@@ -14,12 +14,12 @@
   "deps": {
     "depot_tools": {
       "branch": "master",
-      "revision": "a51a06699f6ccccdd8acab7df70c8e9a9184ff80",
+      "revision": "6aa0bcb82820f0c22ac982941de6bba2d1554729",
       "url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
     },
     "recipe_engine": {
       "branch": "master",
-      "revision": "70031fbf3a131685d9d0f0de3675cb946368743e",
+      "revision": "3f4fe7e32cfe34f02d0244ba6cb73cc5ba58c9be",
       "url": "https://chromium.googlesource.com/infra/luci/recipes-py.git"
     }
   },
diff --git a/infra/cts/run_testlab.go b/infra/cts/run_testlab.go
index 134c230..3e1c9d6 100644
--- a/infra/cts/run_testlab.go
+++ b/infra/cts/run_testlab.go
@@ -26,6 +26,7 @@
 
 	"go.skia.org/infra/go/gcs"
 	"go.skia.org/infra/go/httputils"
+	"go.skia.org/infra/go/skerr"
 
 	"cloud.google.com/go/storage"
 	"google.golang.org/api/option"
@@ -142,14 +143,14 @@
 	cmd.Stdout = &buf
 	cmd.Stderr = io.MultiWriter(os.Stdout, &errBuf)
 	if err := cmd.Run(); err != nil {
-		return nil, nil, sklog.FmtErrorf("Error running: %s\nError:%s\nStdErr:%s", CMD_AVAILABLE_DEVICES, err, errBuf)
+		return nil, nil, skerr.Wrapf(err, "Error running: %s\nStdErr:%s", CMD_AVAILABLE_DEVICES, errBuf)
 	}
 
 	// Unmarshal the result.
 	foundDevices := []*DeviceVersions{}
 	bufBytes := buf.Bytes()
 	if err := json.Unmarshal(bufBytes, &foundDevices); err != nil {
-		return nil, nil, sklog.FmtErrorf("Unmarshal of device information failed: %s \nJSON Input: %s\n", err, string(bufBytes))
+		return nil, nil, skerr.Wrapf(err, "Unmarshal of device information failed: \nJSON Input: %s\n", string(bufBytes))
 	}
 
 	// Filter the devices and copy them to device list.
@@ -261,7 +262,7 @@
 		// Exit code 10 means triggering on Testlab succeeded, but but some of the
 		// runs on devices failed. We consider it a success for this script.
 		if exitCode != 10 {
-			return sklog.FmtErrorf("Error running: %s\nError:%s\nStdErr:%s", cmdStr, err, errBuf)
+			return skerr.Wrapf(err, "Error running: %s\nStdErr:%s", cmdStr, errBuf)
 		}
 	}
 
@@ -311,7 +312,7 @@
 	for _, oneProp := range splitProps {
 		kv := strings.Split(oneProp, "=")
 		if len(kv) != 2 {
-			return nil, sklog.FmtErrorf("Inavlid porperties format. Unable to parse '%s'", propStr)
+			return nil, skerr.Fmt("Invalid properties format. Unable to parse '%s'", propStr)
 		}
 		properties[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
 	}
@@ -358,11 +359,11 @@
 func writeDeviceList(fileName string, devList DeviceList) error {
 	jsonBytes, err := json.MarshalIndent(devList, "", "  ")
 	if err != nil {
-		return sklog.FmtErrorf("Unable to encode JSON: %s", err)
+		return skerr.Wrapf(err, "Unable to encode JSON")
 	}
 
 	if err := ioutil.WriteFile(fileName, jsonBytes, 0644); err != nil {
-		sklog.FmtErrorf("Unable to write file '%s': %s", fileName, err)
+		skerr.Wrapf(err, "Unable to write file '%s'", fileName)
 	}
 	return nil
 }
@@ -370,13 +371,13 @@
 func readDeviceList(fileName string) (DeviceList, error) {
 	inFile, err := os.Open(fileName)
 	if err != nil {
-		return nil, sklog.FmtErrorf("Unable to open file '%s': %s", fileName, err)
+		return nil, skerr.Wrapf(err, "Unable to open file '%s'", fileName)
 	}
 	defer util.Close(inFile)
 
 	var devList DeviceList
 	if err := json.NewDecoder(inFile).Decode(&devList); err != nil {
-		return nil, sklog.FmtErrorf("Unable to decode JSON from '%s': %s", fileName, err)
+		return nil, skerr.Wrapf(err, "Unable to decode JSON from '%s'", fileName)
 	}
 	return devList, nil
 }
diff --git a/infra/gcc/Debian10-mips64el/Dockerfile b/infra/gcc/Debian10-mips64el/Dockerfile
new file mode 100644
index 0000000..24719a5
--- /dev/null
+++ b/infra/gcc/Debian10-mips64el/Dockerfile
@@ -0,0 +1,11 @@
+FROM debian:10-slim
+
+RUN dpkg --add-architecture mips64el && \
+  apt-get update && apt-get upgrade -y && apt-get install -y  \
+  build-essential \
+  ca-certificates \
+  g++-multilib-mips64el-linux-gnuabi64 \
+  libfontconfig-dev:mips64el \
+  libglu-dev:mips64el \
+  python \
+  && rm -rf /var/lib/apt/lists/*
diff --git a/infra/gcc/Debian10-x86/Dockerfile b/infra/gcc/Debian10-x86/Dockerfile
new file mode 100644
index 0000000..ac63a72
--- /dev/null
+++ b/infra/gcc/Debian10-x86/Dockerfile
@@ -0,0 +1,11 @@
+FROM debian:10-slim
+
+RUN dpkg --add-architecture i386 && \
+  apt-get update && apt-get upgrade -y && apt-get install -y  \
+  build-essential \
+  ca-certificates \
+  g++-multilib \
+  libfontconfig-dev:i386 \
+  libglu-dev:i386 \
+  python \
+  && rm -rf /var/lib/apt/lists/*
diff --git a/infra/gcc/Debian10/Dockerfile b/infra/gcc/Debian10/Dockerfile
new file mode 100644
index 0000000..2800ca5
--- /dev/null
+++ b/infra/gcc/Debian10/Dockerfile
@@ -0,0 +1,9 @@
+FROM debian:10-slim
+
+RUN apt-get update && apt-get upgrade -y && apt-get install -y  \
+  build-essential \
+  ca-certificates \
+  libfontconfig-dev \
+  libglu-dev \
+  python \
+  && rm -rf /var/lib/apt/lists/*
diff --git a/infra/gcc/Makefile b/infra/gcc/Makefile
new file mode 100644
index 0000000..91e58b4
--- /dev/null
+++ b/infra/gcc/Makefile
@@ -0,0 +1,16 @@
+
+publish_Debian10:
+	docker build -t gcc-debian10 ./Debian10/
+	docker tag gcc-debian10 gcr.io/skia-public/gcc-debian10
+	docker push gcr.io/skia-public/gcc-debian10
+
+publish_Debian10-x86:
+	docker build -t gcc-debian10-x86 ./Debian10-x86/
+	docker tag gcc-debian10-x86 gcr.io/skia-public/gcc-debian10-x86
+	docker push gcr.io/skia-public/gcc-debian10-x86
+
+publish_Debian10-mips64el:
+	docker build -t gcc-debian10-mips64el ./Debian10-mips64el/
+	docker tag gcc-debian10-mips64el gcr.io/skia-public/gcc-debian10-mips64el
+	docker push gcr.io/skia-public/gcc-debian10-mips64el
+
diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md
index 57cde48..3474800 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -5,11 +5,43 @@
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+
+### Added
+ - `SkContourMeasureIter` and `SkContourMeasure` as an alternative to `SkPathMeasure`.
+ - CanvasKit image decode cache helpers: getDecodeCacheLimitBytes(), setDecodeCacheLimitBytes(),
+   and getDecodeCacheUsedBytes().
+
+### Changed
+ - The returned values from `SkParagraph.getRectsForRange` now have direction with value
+   `CanvasKit.TextDirection`.
+
+## [0.9.0] - 2019-11-18
 ### Added
  - Experimental `CanvasKit.Malloc`, which can be used to create a
    TypedArray backed by the C++ WASM memory. This can save a copy in some cases
    (e.g. SkColorFilter.MakeMatrix). This is an advanced feature, so use it with care.
  - `SkCanvas.clipRRect`, `SkCanvas.drawColor`
+ - Blur, ColorFilter, Compose, MatrixTransform SkImageFilters. Can be used with `SkPaint.setImageFilter`.
+ - `SkCanvas.saveLayer` now takes 3 or 4 params to include up to bounds, paint, SkImageFilter, flags.
+ - `SkPath.rArcTo`, `SkPath.rConicTo`, `SkPath.rCubicTo`, `SkPath.rLineTo`, `SkPath.rMoveTo`,
+   `SkPath.rQuadTo`. Like their non-relative siblings, these are chainable.
+ - Add `width()`, `height()`, `reset()`, `getFrameCount()` to SkAnimatedImage.
+ - `SkCanvas.drawImageNine`, `SkCanvas.drawPoints` and related `PointMode` enum.
+ - `SkPath.addPoly`
+ - `SkPathMeasure.getSegment`
+ - More information on SkParagraph API, eg. `getLongestLine()`, `getWordBoundary`, and others.
+
+### Deprecated
+ - `CanvasKit.MakeBlurMaskFilter` will be renamed/moved soon to `CanvasKit.SkMaskFilter.MakeBlur`.
+
+### Changed
+ - Use newer version of Freetype2 (Tracking Skia's DEPS now).
+ - Use newer versions of libpng and zlib (Tracking Skia's DEPS now).
+
+### Fixed
+ - null dereference when sometimes falling back to CPU.
+ - Actually ask WebGL for a stencil buffer.
+ - Can opt out of Paragraph API with no_paragraph passed into compile.sh or when using primitive_shaper.
 
 ## [0.8.0] - 2019-10-21
 
diff --git a/modules/canvaskit/canvaskit/.gitignore b/modules/canvaskit/canvaskit/.gitignore
index d3c3499..10e23fa 100644
--- a/modules/canvaskit/canvaskit/.gitignore
+++ b/modules/canvaskit/canvaskit/.gitignore
@@ -1,3 +1,4 @@
 bin/
+debug/
 package-lock.json
-node_modules/
\ No newline at end of file
+node_modules/
diff --git a/modules/canvaskit/canvaskit/package.json b/modules/canvaskit/canvaskit/package.json
index 0b86450..89bf0cb 100644
--- a/modules/canvaskit/canvaskit/package.json
+++ b/modules/canvaskit/canvaskit/package.json
@@ -1,6 +1,6 @@
 {
   "name": "canvaskit-wasm",
-  "version": "0.8.0",
+  "version": "0.9.0",
   "description": "A WASM version of Skia's Canvas API",
   "main": "bin/canvaskit.js",
   "homepage": "https://github.com/google/skia/tree/master/modules/canvaskit",
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index f6bb18d..2f42b6f 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -20,6 +20,7 @@
 #include "include/core/SkFontMgr.h"
 #include "include/core/SkFontTypes.h"
 #include "include/core/SkImage.h"
+#include "include/core/SkImageFilter.h"
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkMaskFilter.h"
 #include "include/core/SkPaint.h"
@@ -43,6 +44,7 @@
 #include "include/effects/SkDashPathEffect.h"
 #include "include/effects/SkDiscretePathEffect.h"
 #include "include/effects/SkGradientShader.h"
+#include "include/effects/SkImageFilters.h"
 #include "include/effects/SkTrimPathEffect.h"
 #include "include/pathops/SkPathOps.h"
 #include "include/utils/SkParsePath.h"
@@ -50,6 +52,7 @@
 #include "modules/skshaper/include/SkShaper.h"
 #include "src/core/SkFontMgrPriv.h"
 #include "src/core/SkMakeUnique.h"
+#include "src/core/SkResourceCache.h"
 
 #include <iostream>
 #include <string>
@@ -140,10 +143,13 @@
     info.fFBOID = (GrGLuint) buffer;
     SkColorType colorType;
 
+    GrGLint stencil;
+    glGetIntegerv(GL_STENCIL_BITS, &stencil);
+
     info.fFormat = GL_RGBA8;
     colorType = kRGBA_8888_SkColorType;
 
-    GrBackendRenderTarget target(width, height, 0, 8, info);
+    GrBackendRenderTarget target(width, height, 0, stencil, info);
 
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
                                                                     kBottomLeft_GrSurfaceOrigin,
@@ -188,8 +194,7 @@
 }
 
 void ApplyAddOval(SkPath& orig, const SkRect& oval, bool ccw, unsigned start) {
-    orig.addOval(oval, ccw ? SkPath::Direction::kCCW_Direction :
-                             SkPath::Direction::kCW_Direction, start);
+    orig.addOval(oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start);
 }
 
 void ApplyAddPath(SkPath& orig, const SkPath& newPath,
@@ -206,9 +211,7 @@
 
 void ApplyAddRect(SkPath& path, SkScalar left, SkScalar top,
                   SkScalar right, SkScalar bottom, bool ccw) {
-    path.addRect(left, top, right, bottom,
-                 ccw ? SkPath::Direction::kCCW_Direction :
-                 SkPath::Direction::kCW_Direction);
+    path.addRect(left, top, right, bottom, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
 }
 
 void ApplyAddRoundRect(SkPath& path, SkScalar left, SkScalar top,
@@ -217,7 +220,7 @@
     // See comment below for uintptr_t explanation
     const SkScalar* radii = reinterpret_cast<const SkScalar*>(rPtr);
     path.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radii,
-                      ccw ? SkPath::Direction::kCCW_Direction : SkPath::Direction::kCW_Direction);
+                      ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
 }
 
 
@@ -230,13 +233,20 @@
     p.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
 }
 
-void ApplyAddArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
-                          bool useSmallArc, bool ccw, SkScalar x, SkScalar y) {
+void ApplyArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
+                       bool useSmallArc, bool ccw, SkScalar x, SkScalar y) {
     auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
-    auto sweep = ccw ? SkPath::Direction::kCCW_Direction : SkPath::Direction::kCW_Direction;
+    auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
     orig.arcTo(rx, ry, xAxisRotate, arcSize, sweep, x, y);
 }
 
+void ApplyRArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
+                        bool useSmallArc, bool ccw, SkScalar dx, SkScalar dy) {
+    auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
+    auto sweep = ccw ? SkPathDirection::kCCW : SkPathDirection::kCW;
+    orig.rArcTo(rx, ry, xAxisRotate, arcSize, sweep, dx, dy);
+}
+
 void ApplyClose(SkPath& p) {
     p.close();
 }
@@ -246,19 +256,37 @@
     p.conicTo(x1, y1, x2, y2, w);
 }
 
+void ApplyRConicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
+                  SkScalar w) {
+    p.rConicTo(dx1, dy1, dx2, dy2, w);
+}
+
 void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
                   SkScalar x3, SkScalar y3) {
     p.cubicTo(x1, y1, x2, y2, x3, y3);
 }
 
+void ApplyRCubicTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
+                  SkScalar dx3, SkScalar dy3) {
+    p.rCubicTo(dx1, dy1, dx2, dy2, dx3, dy3);
+}
+
 void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
     p.lineTo(x, y);
 }
 
+void ApplyRLineTo(SkPath& p, SkScalar dx, SkScalar dy) {
+    p.rLineTo(dx, dy);
+}
+
 void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
     p.moveTo(x, y);
 }
 
+void ApplyRMoveTo(SkPath& p, SkScalar dx, SkScalar dy) {
+    p.rMoveTo(dx, dy);
+}
+
 void ApplyReset(SkPath& p) {
     p.reset();
 }
@@ -271,6 +299,10 @@
     p.quadTo(x1, y1, x2, y2);
 }
 
+void ApplyRQuadTo(SkPath& p, SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2) {
+    p.rQuadTo(dx1, dy1, dx2, dy2);
+}
+
 void ApplyTransform(SkPath& orig,
                     SkScalar scaleX, SkScalar skewX,  SkScalar transX,
                     SkScalar skewY,  SkScalar scaleY, SkScalar transY,
@@ -557,10 +589,20 @@
 };
 
 void drawShapedText(SkCanvas& canvas, ShapedText st, SkScalar x,
-                     SkScalar y, SkPaint paint) {
+                    SkScalar y, SkPaint paint) {
     canvas.drawTextBlob(st.blob(), x, y, paint);
 }
 
+int saveLayerRec(SkCanvas& canvas, const SkPaint* paint,
+                 const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
+    return canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, flags));
+}
+
+int saveLayerRecBounds(SkCanvas& canvas, const SkPaint* paint, const SkImageFilter* backdrop,
+                       SkCanvas::SaveLayerFlags flags, const SkRect& bounds) {
+    return canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, flags));
+}
+
 // This is simpler than dealing with an SkPoint and SkVector
 struct PosTan {
     SkScalar px, py, tx, ty;
@@ -608,6 +650,10 @@
         void raw_destructor(ClassType *);
 
         template<>
+        void raw_destructor<SkContourMeasure>(SkContourMeasure *ptr) {
+        }
+
+        template<>
         void raw_destructor<SkData>(SkData *ptr) {
         }
 
@@ -649,6 +695,10 @@
 
     constant("gpu", true);
 #endif
+    function("getDecodeCacheLimitBytes", &SkResourceCache::GetTotalByteLimit);
+    function("setDecodeCacheLimitBytes", &SkResourceCache::SetTotalByteLimit);
+    function("getDecodeCacheUsedBytes" , &SkResourceCache::GetTotalBytesUsed);
+
     function("computeTonalColors", &computeTonalColors);
     function("_decodeAnimatedImage", optional_override([](uintptr_t /* uint8_t*  */ iptr,
                                                   size_t length)->sk_sp<SkAnimatedImage> {
@@ -680,6 +730,7 @@
     function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
     function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
     function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
+    // Deprecated: use Canvaskit.SkMaskFilter.MakeBlur
     function("MakeBlurMaskFilter", optional_override([](SkBlurStyle style, SkScalar sigma, bool respectCTM)->sk_sp<SkMaskFilter> {
         // Adds a little helper because emscripten doesn't expose default params.
         return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
@@ -805,14 +856,22 @@
 
     class_<SkAnimatedImage>("SkAnimatedImage")
         .smart_ptr<sk_sp<SkAnimatedImage>>("sk_sp<SkAnimatedImage>")
+        .function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame)
+        .function("getFrameCount", &SkAnimatedImage::getFrameCount)
         .function("getRepetitionCount", &SkAnimatedImage::getRepetitionCount)
-        .function("decodeNextFrame", &SkAnimatedImage::decodeNextFrame);
+        .function("height",  optional_override([](SkAnimatedImage& self)->int32_t {
+            return self.dimensions().height();
+        }))
+        .function("reset", &SkAnimatedImage::reset)
+        .function("width",  optional_override([](SkAnimatedImage& self)->int32_t {
+            return self.dimensions().width();
+        }));
 
     class_<SkCanvas>("SkCanvas")
         .constructor<>()
         .function("clear", &SkCanvas::clear)
         .function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
-        .function("clipRRect",optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
+        .function("clipRRect", optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
             self.clipRRect(toRRect(r), op, doAntiAlias);
         }))
         .function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
@@ -839,10 +898,15 @@
             self.drawDRRect(toRRect(o), toRRect(i), paint);
         }))
         .function("drawAnimatedImage",  optional_override([](SkCanvas& self, sk_sp<SkAnimatedImage>& aImg,
-                                                        SkScalar x, SkScalar y)->void {
+                                                             SkScalar x, SkScalar y)->void {
             self.drawDrawable(aImg.get(), x, y);
         }), allow_raw_pointers())
         .function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
+        .function("drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
+                                                        SkIRect center, SkRect dst,
+                                                        const SkPaint* paint)->void {
+            self.drawImageNine(image, center, dst, paint);
+        }), allow_raw_pointers())
         .function("drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
                                                         SkRect src, SkRect dst,
                                                         const SkPaint* paint, bool fastSample)->void {
@@ -863,6 +927,13 @@
         // Of note, picture is *not* what is colloquially thought of as a "picture", what we call
         // a bitmap. An SkPicture is a series of draw commands.
         .function("drawPicture",  select_overload<void (const sk_sp<SkPicture>&)>(&SkCanvas::drawPicture))
+        .function("_drawPoints", optional_override([](SkCanvas& self, SkCanvas::PointMode mode,
+                                                     uintptr_t /* SkPoint* */ pptr,
+                                                     int count, SkPaint paint)->void {
+            // See comment above for uintptr_t explanation
+            const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
+            self.drawPoints(mode, count, pts, paint);
+        }))
         .function("drawRRect",optional_override([](SkCanvas& self, const SimpleRRect& r, const SkPaint& paint) {
             self.drawRRect(toRRect(r), paint);
         }))
@@ -910,8 +981,14 @@
         .function("restoreToCount", &SkCanvas::restoreToCount)
         .function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
         .function("save", &SkCanvas::save)
+         // 2 params
         .function("saveLayer", select_overload<int (const SkRect&, const SkPaint*)>(&SkCanvas::saveLayer),
                                allow_raw_pointers())
+         // 3 params (effectively with SaveLayerRec, but no bounds)
+        .function("saveLayer", saveLayerRec, allow_raw_pointers())
+         // 4 params (effectively with SaveLayerRec)
+        .function("saveLayer", saveLayerRecBounds, allow_raw_pointers())
+
         .function("scale", &SkCanvas::scale)
         .function("skew", &SkCanvas::skew)
         .function("translate", &SkCanvas::translate)
@@ -936,6 +1013,33 @@
         }))
         .class_function("MakeSRGBToLinearGamma", &SkColorFilters::SRGBToLinearGamma);
 
+    class_<SkContourMeasureIter>("SkContourMeasureIter")
+        .constructor<const SkPath&, bool, SkScalar>()
+        .function("next", &SkContourMeasureIter::next);
+
+    class_<SkContourMeasure>("SkContourMeasure")
+        .smart_ptr<sk_sp<SkContourMeasure>>("sk_sp<SkContourMeasure>>")
+        .function("getPosTan", optional_override([](SkContourMeasure& self,
+                                                    SkScalar distance) -> PosTan {
+            SkPoint p{0, 0};
+            SkVector v{0, 0};
+            if (!self.getPosTan(distance, &p, &v)) {
+                SkDebugf("zero-length path in getPosTan\n");
+            }
+            return PosTan{p.x(), p.y(), v.x(), v.y()};
+        }))
+        .function("getSegment", optional_override([](SkContourMeasure& self, SkScalar startD,
+                                                     SkScalar stopD, bool startWithMoveTo) -> SkPath {
+            SkPath p;
+            bool ok = self.getSegment(startD, stopD, &p, startWithMoveTo);
+            if (ok) {
+                return p;
+            }
+            return SkPath();
+        }))
+        .function("isClosed", &SkContourMeasure::isClosed)
+        .function("length", &SkContourMeasure::length);
+
     class_<SkData>("SkData")
         .smart_ptr<sk_sp<SkData>>("sk_sp<SkData>>")
         .function("size", &SkData::size);
@@ -1050,8 +1154,30 @@
             return self->readPixels(ii, pixels, dstRowBytes, srcX, srcY);
         }), allow_raw_pointers());
 
+    class_<SkImageFilter>("SkImageFilter")
+        .smart_ptr<sk_sp<SkImageFilter>>("sk_sp<SkImageFilter>")
+        .class_function("MakeBlur", optional_override([](SkScalar sigmaX, SkScalar sigmaY,
+                                                         SkTileMode tileMode, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
+            // Emscripten does not like default args nor SkIRect* much
+            return SkImageFilters::Blur(sigmaX, sigmaY, tileMode, input);
+        }))
+        .class_function("MakeColorFilter", optional_override([](sk_sp<SkColorFilter> cf,
+                                                                  sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
+            // Emscripten does not like default args nor SkIRect* much
+            return SkImageFilters::ColorFilter(cf, input);
+        }))
+        .class_function("MakeCompose", &SkImageFilters::Compose)
+        .class_function("MakeMatrixTransform", optional_override([](SimpleMatrix sm, SkFilterQuality fq,
+                                                                   sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
+            return SkImageFilters::MatrixTransform(toSkMatrix(sm), fq, input);
+        }));
+
     class_<SkMaskFilter>("SkMaskFilter")
-        .smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<SkMaskFilter>");
+        .smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<SkMaskFilter>")
+        .class_function("MakeBlur", optional_override([](SkBlurStyle style, SkScalar sigma, bool respectCTM)->sk_sp<SkMaskFilter> {
+        // Adds a little helper because emscripten doesn't expose default params.
+        return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
+    }), allow_raw_pointers());
 
     class_<SkPaint>("SkPaint")
         .constructor<>()
@@ -1077,6 +1203,7 @@
         }))
         .function("setColorFilter", &SkPaint::setColorFilter)
         .function("setFilterQuality", &SkPaint::setFilterQuality)
+        .function("setImageFilter", &SkPaint::setImageFilter)
         .function("setMaskFilter", &SkPaint::setMaskFilter)
         .function("setPathEffect", &SkPaint::setPathEffect)
         .function("setShader", &SkPaint::setShader)
@@ -1096,13 +1223,20 @@
         // interface.js has 3 overloads of addPath
         .function("_addOval", &ApplyAddOval)
         .function("_addPath", &ApplyAddPath)
+        .function("_addPoly", optional_override([](SkPath& self,
+                                                   uintptr_t /* SkPoint* */ pptr,
+                                                   int count, bool close)->void {
+            // See comment above for uintptr_t explanation
+            const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
+            self.addPoly(pts, count, close);
+        }))
         // interface.js has 4 overloads of addRect
         .function("_addRect", &ApplyAddRect)
         // interface.js has 4 overloads of addRoundRect
         .function("_addRoundRect", &ApplyAddRoundRect)
         .function("_arcTo", &ApplyArcTo)
         .function("_arcTo", &ApplyArcToAngle)
-        .function("_arcTo", &ApplyAddArcToArcSize)
+        .function("_arcTo", &ApplyArcToArcSize)
         .function("_close", &ApplyClose)
         .function("_conicTo", &ApplyConicTo)
         .function("countPoints", &SkPath::countPoints)
@@ -1113,9 +1247,15 @@
         .function("isVolatile", &SkPath::isVolatile)
         .function("_lineTo", &ApplyLineTo)
         .function("_moveTo", &ApplyMoveTo)
+        .function("_quadTo", &ApplyQuadTo)
+        .function("_rArcTo", &ApplyRArcToArcSize)
+        .function("_rConicTo", &ApplyRConicTo)
+        .function("_rCubicTo", &ApplyRCubicTo)
+        .function("_rLineTo", &ApplyRLineTo)
+        .function("_rMoveTo", &ApplyRMoveTo)
+        .function("_rQuadTo", &ApplyRQuadTo)
         .function("reset", &ApplyReset)
         .function("rewind", &ApplyRewind)
-        .function("_quadTo", &ApplyQuadTo)
         .function("setIsVolatile", &SkPath::setIsVolatile)
         .function("_transform", select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
 
@@ -1132,7 +1272,7 @@
         .function("toSVGString", &ToSVGString)
         .function("toCmds", &ToCmds)
 
-        .function("setFillType", &SkPath::setFillType)
+        .function("setFillType", select_overload<void(SkPathFillType)>(&SkPath::setFillType))
         .function("getFillType", &SkPath::getFillType)
         .function("getBounds", &SkPath::getBounds)
         .function("computeTightBounds", &SkPath::computeTightBounds)
@@ -1156,6 +1296,15 @@
             }
             return PosTan{p.x(), p.y(), v.x(), v.y()};
         }))
+        .function("getSegment", optional_override([](SkPathMeasure& self, SkScalar startD,
+                                                     SkScalar stopD, bool startWithMoveTo) -> SkPath {
+            SkPath p;
+            bool ok = self.getSegment(startD, stopD, &p, startWithMoveTo);
+            if (ok) {
+                return p;
+            }
+            return SkPath();
+        }))
         .function("isClosed", &SkPathMeasure::isClosed)
         .function("nextContour", &SkPathMeasure::nextContour);
 
@@ -1337,11 +1486,11 @@
         .value("R16G16_float", SkColorType::kR16G16_float_SkColorType)
         .value("R16G16B16A16_unorm", SkColorType::kR16G16B16A16_unorm_SkColorType);
 
-    enum_<SkPath::FillType>("FillType")
-        .value("Winding",           SkPath::FillType::kWinding_FillType)
-        .value("EvenOdd",           SkPath::FillType::kEvenOdd_FillType)
-        .value("InverseWinding",    SkPath::FillType::kInverseWinding_FillType)
-        .value("InverseEvenOdd",    SkPath::FillType::kInverseEvenOdd_FillType);
+    enum_<SkPathFillType>("FillType")
+        .value("Winding",           SkPathFillType::kWinding)
+        .value("EvenOdd",           SkPathFillType::kEvenOdd)
+        .value("InverseWinding",    SkPathFillType::kInverseWinding)
+        .value("InverseEvenOdd",    SkPathFillType::kInverseEvenOdd);
 
     enum_<SkFilterQuality>("FilterQuality")
         .value("None",   SkFilterQuality::kNone_SkFilterQuality)
@@ -1365,6 +1514,11 @@
         .value("XOR",                SkPathOp::kXOR_SkPathOp)
         .value("ReverseDifference",  SkPathOp::kReverseDifference_SkPathOp);
 
+    enum_<SkCanvas::PointMode>("PointMode")
+        .value("Points",   SkCanvas::PointMode::kPoints_PointMode)
+        .value("Lines",    SkCanvas::PointMode::kLines_PointMode)
+        .value("Polygon",  SkCanvas::PointMode::kPolygon_PointMode);
+
     enum_<SkPaint::Cap>("StrokeCap")
         .value("Butt",   SkPaint::Cap::kButt_Cap)
         .value("Round",  SkPaint::Cap::kRound_Cap)
@@ -1502,4 +1656,8 @@
     constant("CONIC_VERB", CONIC);
     constant("CUBIC_VERB", CUBIC);
     constant("CLOSE_VERB", CLOSE);
+
+    constant("SaveLayerInitWithPrevious", SkCanvas::SaveLayerFlagsSet::kInitWithPrevious_SaveLayerFlag);
+    constant("SaveLayerF16ColorType",     SkCanvas::SaveLayerFlagsSet::kF16ColorType);
+
 }
diff --git a/modules/canvaskit/compile.sh b/modules/canvaskit/compile.sh
index a39852e..c89cfde 100755
--- a/modules/canvaskit/compile.sh
+++ b/modules/canvaskit/compile.sh
@@ -72,19 +72,22 @@
 
 MANAGED_SKOTTIE_BINDINGS="\
   -DSK_INCLUDE_MANAGED_SKOTTIE=1 \
-  modules/skottie/utils/SkottieUtils.cpp"
+  modules/skottie/utils/SkottieUtils.cpp \
+  modules/skresources/src/SkResources.cpp"
 if [[ $@ == *no_managed_skottie* ]]; then
   echo "Omitting managed Skottie"
   MANAGED_SKOTTIE_BINDINGS="-DSK_INCLUDE_MANAGED_SKOTTIE=0"
 fi
 
 GN_PARTICLES="skia_enable_sksl_interpreter=true"
+PARTICLES_JS="--pre-js $BASE_DIR/particles.js"
 PARTICLES_BINDINGS="$BASE_DIR/particles_bindings.cpp"
 PARTICLES_LIB="$BUILD_DIR/libparticles.a"
 
 if [[ $@ == *no_particles* ]]; then
   echo "Omitting Particles"
   GN_PARTICLES="skia_enable_sksl_interpreter=false"
+  PARTICLES_JS=""
   PARTICLES_BINDINGS=""
   PARTICLES_LIB=""
 fi
@@ -127,7 +130,7 @@
       --align 4
 fi
 
-GN_SHAPER="skia_use_icu=true skia_use_system_icu=false skia_use_system_harfbuzz=false"
+GN_SHAPER="skia_use_icu=true skia_use_system_icu=false skia_use_harfbuzz=true skia_use_system_harfbuzz=false"
 SHAPER_LIB="$BUILD_DIR/libharfbuzz.a \
             $BUILD_DIR/libicu.a"
 SHAPER_TARGETS="libharfbuzz.a libicu.a"
@@ -143,6 +146,14 @@
 PARAGRAPH_BINDINGS="-DSK_INCLUDE_PARAGRAPH=1 \
   $BASE_DIR/paragraph_bindings.cpp"
 
+if [[ $@ == *no_paragraph* ]] || [[ $@ == *primitive_shaper* ]]; then
+  echo "Omitting paragraph (must also have non-primitive shaper)"
+  PARAGRAPH_JS=""
+  PARAGRAPH_LIB=""
+  PARAGRAPH_BINDINGS=""
+fi
+
+
 # Turn off exiting while we check for ninja (which may not be on PATH)
 set +e
 NINJA=`which ninja`
@@ -163,7 +174,7 @@
   cxx=\"${EMCXX}\" \
   ar=\"${EMAR}\" \
   extra_cflags_cc=[\"-frtti\"] \
-  extra_cflags=[\"-s\",\"USE_FREETYPE=1\",\"-s\",\"USE_LIBPNG=1\", \"-s\", \"WARN_UNALIGNED=1\",
+  extra_cflags=[\"-s\", \"WARN_UNALIGNED=1\",
     \"-DSKNX_NO_SIMD\", \"-DSK_DISABLE_AAA\", \"-DSK_DISABLE_READBUFFER\",
     \"-DSK_DISABLE_EFFECT_DESERIALIZATION\",
     ${GN_GPU_FLAGS}
@@ -188,11 +199,12 @@
   skia_use_libwebp=false \
   skia_use_lua=false \
   skia_use_piex=false \
-  skia_use_system_libpng=true \
-  skia_use_system_freetype2=true \
+  skia_use_system_libpng=false \
+  skia_use_system_freetype2=false \
   skia_use_system_libjpeg_turbo=false \
+  skia_use_system_zlib=false\
   skia_use_vulkan=false \
-  skia_use_wuffs = true \
+  skia_use_wuffs=true \
   skia_use_zlib=true \
   \
   ${GN_SHAPER} \
@@ -234,6 +246,7 @@
     --pre-js $BASE_DIR/interface.js \
     $PARAGRAPH_JS \
     $SKOTTIE_JS \
+    $PARTICLES_JS \
     $HTML_CANVAS_API \
     --pre-js $BASE_DIR/postamble.js \
     --post-js $BASE_DIR/ready.js \
@@ -256,8 +269,6 @@
     -s NO_EXIT_RUNTIME=1 \
     -s STRICT=1 \
     -s TOTAL_MEMORY=128MB \
-    -s USE_FREETYPE=1 \
-    -s USE_LIBPNG=1 \
     -s WARN_UNALIGNED=1 \
     -s WASM=1 \
     -o $BUILD_DIR/canvaskit.js
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index c3fc817..c071df1 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -53,6 +53,7 @@
 	MakeRenderTarget: function() {},
 	MakeSWCanvasSurface: function() {},
 	MakeManagedAnimation: function() {},
+	MakeParticles: function() {},
 	MakeSkDashPathEffect: function() {},
 	MakeSkVertices: function() {},
 	MakeSurface: function() {},
@@ -65,9 +66,12 @@
 	computeTonalColors: function() {},
 	currentContext: function() {},
 	getColorComponents: function() {},
+	getDecodeCacheLimitBytes: function() {},
+	getDecodeCacheUsageBytes: function() {},
 	getSkDataBytes: function() {},
 	multiplyByAlpha: function() {},
 	setCurrentContext: function() {},
+	setDecodeCacheLimitBytes: function() {},
 
 	// private API (i.e. things declared in the bindings that we use
 	// in the pre-js file)
@@ -76,6 +80,7 @@
 	_MakePathFromCmds: function() {},
 	_MakeRadialGradientShader: function() {},
 	_MakeManagedAnimation: function() {},
+	_MakeParticles: function() {},
 	_MakeSkDashPathEffect: function() {},
 	_MakeSkVertices: function() {},
 	_MakeTwoPointConicalGradientShader: function() {},
@@ -100,7 +105,16 @@
 
 	Paragraph: {
 		// public API (from C++ bindings)
+		didExceedMaxLines: function() {},
+		getAlphabeticBaseline: function() {},
 		getGlyphPositionAtCoordinate: function() {},
+		getHeight: function() {},
+		getIdeographicBaseline: function() {},
+		getLongestLine: function() {},
+		getMaxIntrinsicWidth: function() {},
+		getMaxWidth: function() {},
+		getMinIntrinsicWidth: function() {},
+		getWordBoundary: function() {},
 		layout: function() {},
 
 		// private API
@@ -109,7 +123,6 @@
 	},
 
 	ParagraphStyle: function() {},
-
 	RSXFormBuilder: function() {},
 	SkColorBuilder: function() {},
 	SkRectBuilder: function() {},
@@ -121,8 +134,12 @@
 
 	SkAnimatedImage: {
 		// public API (from C++ bindings)
-		getRepetitionCount: function() {},
 		decodeNextFrame: function() {},
+		getFrameCount: function() {},
+		getRepetitionCount: function() {},
+		height: function() {},
+		reset: function() {},
+		width: function() {},
 	},
 
 	SkCanvas: {
@@ -138,6 +155,7 @@
 		drawColor: function() {},
 		drawDRRect:  function() {},
 		drawImage: function() {},
+		drawImageNine: function() {},
 		drawImageRect: function() {},
 		drawLine: function() {},
 		drawOval: function() {},
@@ -167,6 +185,7 @@
 
 		// private API
 		_drawAtlas: function() {},
+		_drawPoints: function() {},
 		_drawSimpleText: function() {},
 		_readPixels: function() {},
 		_writePixels: function() {},
@@ -193,6 +212,17 @@
 		scaled: function() {},
 	},
 
+	SkContourMeasureIter: {
+		next: function() {},
+	},
+
+	SkContourMeasure: {
+		getPosTan: function() {},
+		getSegment: function() {},
+		isClosed: function() {},
+		length: function() {},
+	},
+
 	SkFont: {
 		// public API (from C++ bindings)
 		getScaleX: function() {},
@@ -229,6 +259,13 @@
 		_makeShader: function() {},
 	},
 
+	SkImageFilter: {
+		MakeBlur: function() {},
+		MakeColorFilter: function() {},
+		MakeCompose: function() {},
+		MakeMatrixTransform: function() {},
+	},
+
 	SkMatrix: {
 		identity: function() {},
 		invert: function() {},
@@ -240,6 +277,10 @@
 		translated: function() {},
 	},
 
+	SkMaskFilter: {
+		MakeBlur: function() {},
+	},
+
 	SkPaint: {
 		// public API (from C++ bindings)
 		/** @return {CanvasKit.SkPaint} */
@@ -255,6 +296,7 @@
 		setBlendMode: function() {},
 		setColor: function() {},
 		setFilterQuality: function() {},
+		setImageFilter: function() {},
 		setMaskFilter: function() {},
 		setPathEffect: function() {},
 		setShader: function() {},
@@ -292,6 +334,7 @@
 		_addOval: function() {},
 		_addPath: function() {},
 		_addRect: function() {},
+		_addPoly: function() {},
 		_addRoundRect: function() {},
 		_arc: function() {},
 		_arcTo: function() {},
@@ -303,6 +346,12 @@
 		_moveTo: function() {},
 		_op: function() {},
 		_quadTo: function() {},
+		_rArcTo: function() {},
+		_rConicTo: function() {},
+		_rCubicTo: function() {},
+		_rLineTo: function() {},
+		_rMoveTo: function() {},
+		_rQuadTo: function() {},
 		_rect: function() {},
 		_simplify: function() {},
 		_stroke: function() {},
@@ -315,6 +364,7 @@
 
 	SkPathMeasure: {
 		getLength: function() {},
+		getSegment: function() {},
 		getPosTan: function() {},
 		isClosed: function() {},
 		nextContour: function() {},
@@ -422,6 +472,9 @@
 	OverlineDecoration: {},
 	LineThroughDecoration: {},
 
+	SaveLayerInitWithPrevious: {},
+	SaveLayerF16ColorType: {},
+
 	Affinity: {
 		Upstream: {},
 		Downstream: {},
@@ -556,9 +609,18 @@
 		ReverseDifference: {},
 	},
 
+	PointMode: {
+		Points: {},
+		Lines: {},
+		Polygon: {},
+	},
+
 	RectHeightStyle: {
 		Tight: {},
 		Max: {},
+		IncludeLineSpacingMiddle: {},
+		IncludeLineSpacingTop: {},
+		IncludeLineSpacingBottom: {},
 	},
 
 	RectWidthStyle: {
@@ -660,6 +722,7 @@
 CanvasKit.SkPath.prototype.addArc = function() {};
 CanvasKit.SkPath.prototype.addOval = function() {};
 CanvasKit.SkPath.prototype.addPath = function() {};
+CanvasKit.SkPath.prototype.addPoly = function() {};
 CanvasKit.SkPath.prototype.addRect = function() {};
 CanvasKit.SkPath.prototype.addRoundRect = function() {};
 CanvasKit.SkPath.prototype.arc = function() {};
@@ -673,6 +736,12 @@
 CanvasKit.SkPath.prototype.offset = function() {};
 CanvasKit.SkPath.prototype.op = function() {};
 CanvasKit.SkPath.prototype.quadTo = function() {};
+CanvasKit.SkPath.prototype.rArcTo = function() {};
+CanvasKit.SkPath.prototype.rConicTo = function() {};
+CanvasKit.SkPath.prototype.rCubicTo = function() {};
+CanvasKit.SkPath.prototype.rLineTo = function() {};
+CanvasKit.SkPath.prototype.rMoveTo = function() {};
+CanvasKit.SkPath.prototype.rQuadTo = function() {};
 CanvasKit.SkPath.prototype.rect = function() {};
 CanvasKit.SkPath.prototype.simplify = function() {};
 CanvasKit.SkPath.prototype.stroke = function() {};
@@ -693,6 +762,7 @@
 CanvasKit.SkImage.prototype.makeShader = function() {};
 
 CanvasKit.SkCanvas.prototype.drawAtlas = function() {};
+CanvasKit.SkCanvas.prototype.drawPoints = function() {};
 CanvasKit.SkCanvas.prototype.drawText = function() {};
 /** @return {Uint8Array} */
 CanvasKit.SkCanvas.prototype.readPixels = function() {};
diff --git a/modules/canvaskit/gpu.js b/modules/canvaskit/gpu.js
index 179b64b..3863c04 100644
--- a/modules/canvaskit/gpu.js
+++ b/modules/canvaskit/gpu.js
@@ -11,11 +11,10 @@
       }
 
       function makeWebGLContext(canvas, attrs) {
-        // These defaults come from the emscripten _emscripten_webgl_create_context
         var contextAttributes = {
           alpha: get(attrs, 'alpha', 1),
           depth: get(attrs, 'depth', 1),
-          stencil: get(attrs, 'stencil', 0),
+          stencil: get(attrs, 'stencil', 8),
           antialias: get(attrs, 'antialias', 1),
           premultipliedAlpha: get(attrs, 'premultipliedAlpha', 1),
           preserveDrawingBuffer: get(attrs, 'preserveDrawingBuffer', 0),
@@ -80,9 +79,12 @@
 
         var grcontext = this.MakeGrContext(ctx);
 
-        // Bump the default resource cache limit.
-        var RESOURCE_CACHE_BYTES = 256 * 1024 * 1024;
-        grcontext.setResourceCacheLimitBytes(RESOURCE_CACHE_BYTES);
+        if (grcontext) {
+           // Bump the default resource cache limit.
+          var RESOURCE_CACHE_BYTES = 256 * 1024 * 1024;
+          grcontext.setResourceCacheLimitBytes(RESOURCE_CACHE_BYTES);
+        }
+
 
         // Maybe better to use clientWidth/height.  See:
         // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
diff --git a/modules/canvaskit/helper.js b/modules/canvaskit/helper.js
index de2178a..698e7e0 100644
--- a/modules/canvaskit/helper.js
+++ b/modules/canvaskit/helper.js
@@ -91,7 +91,7 @@
 }
 
 // arr should be a non-jagged 2d JS array (TypedArrays can't be nested
-//     inside themselves.)
+//     inside themselves). A common use case is points.
 // dest is something like CanvasKit.HEAPF32
 // ptr can be optionally provided if the memory was already allocated.
 function copy2dArray(arr, dest, ptr) {
diff --git a/modules/canvaskit/htmlcanvas/canvas2dcontext.js b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
index 350ff30..7c08dff 100644
--- a/modules/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/modules/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -1015,7 +1015,7 @@
     }
     var shadowPaint = basePaint.copy();
     shadowPaint.setColor(alphaColor);
-    var blurEffect = CanvasKit.MakeBlurMaskFilter(CanvasKit.BlurStyle.Normal,
+    var blurEffect = CanvasKit.SkMaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal,
       SkBlurRadiusToSigma(this._shadowBlur),
       false);
     shadowPaint.setMaskFilter(blurEffect);
diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js
index 7eea323..a859cbd 100644
--- a/modules/canvaskit/interface.js
+++ b/modules/canvaskit/interface.js
@@ -273,6 +273,26 @@
     return this;
   };
 
+  // points is either an array of [x, y] where x and y are numbers or
+  // a typed array from Malloc where the even indices will be treated
+  // as x coordinates and the odd indices will be treated as y coordinates.
+  CanvasKit.SkPath.prototype.addPoly = function(points, close) {
+    var ptr;
+    var n;
+    // This was created with CanvasKit.Malloc, so assume the user has
+    // already been filled with data.
+    if (points['_ck']) {
+      ptr = points.byteOffset;
+      n = points.length/2;
+    } else {
+      ptr = copy2dArray(points, CanvasKit.HEAPF32);
+      n = points.length;
+    }
+    this._addPoly(ptr, n, close);
+    CanvasKit._free(ptr);
+    return this;
+  };
+
   CanvasKit.SkPath.prototype.addRect = function() {
     // Takes 1, 2, 4 or 5 args
     //  - SkRect
@@ -410,6 +430,38 @@
     return this;
   };
 
+ CanvasKit.SkPath.prototype.rArcTo = function(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy) {
+    this._rArcTo(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy);
+    return this;
+  };
+
+  CanvasKit.SkPath.prototype.rConicTo = function(dx1, dy1, dx2, dy2, w) {
+    this._rConicTo(dx1, dy1, dx2, dy2, w);
+    return this;
+  };
+
+  // These params are all relative
+  CanvasKit.SkPath.prototype.rCubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
+    this._rCubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
+    return this;
+  };
+
+  CanvasKit.SkPath.prototype.rLineTo = function(dx, dy) {
+    this._rLineTo(dx, dy);
+    return this;
+  };
+
+  CanvasKit.SkPath.prototype.rMoveTo = function(dx, dy) {
+    this._rMoveTo(dx, dy);
+    return this;
+  };
+
+  // These params are all relative
+  CanvasKit.SkPath.prototype.rQuadTo = function(cpx, cpy, x, y) {
+    this._rQuadTo(cpx, cpy, x, y);
+    return this;
+  };
+
   CanvasKit.SkPath.prototype.simplify = function() {
     if (this._simplify()) {
       return this;
@@ -595,6 +647,25 @@
 
   }
 
+  // points is either an array of [x, y] where x and y are numbers or
+  // a typed array from Malloc where the even indices will be treated
+  // as x coordinates and the odd indices will be treated as y coordinates.
+  CanvasKit.SkCanvas.prototype.drawPoints = function(mode, points, paint) {
+    var ptr;
+    var n;
+    // This was created with CanvasKit.Malloc, so assume the user has
+    // already been filled with data.
+    if (points['_ck']) {
+      ptr = points.byteOffset;
+      n = points.length/2;
+    } else {
+      ptr = copy2dArray(points, CanvasKit.HEAPF32);
+      n = points.length;
+    }
+    this._drawPoints(mode, ptr, n, paint);
+    CanvasKit._free(ptr);
+  }
+
   // str can be either a text string or a ShapedText object
   CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, paint, font) {
     if (typeof str === 'string') {
diff --git a/modules/canvaskit/karma.bench.conf.js b/modules/canvaskit/karma.bench.conf.js
index d4900b8..1179f72 100644
--- a/modules/canvaskit/karma.bench.conf.js
+++ b/modules/canvaskit/karma.bench.conf.js
@@ -61,12 +61,18 @@
     cfg.browsers = ['ChromeHeadlessNoSandbox'],
     cfg.customLaunchers = {
         ChromeHeadlessNoSandbox: {
-            base: 'ChromeHeadless',
-            flags: [
+          base: 'ChromeHeadless',
+          flags: [
             // Without this flag, we see an error:
             // Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted
-                '--no-sandbox'
-            ],
+            '--no-sandbox',
+            // may help tests be less flaky
+            // https://peter.sh/experiments/chromium-command-line-switches/#browser-test
+            '--browser-test',
+            // This can also help avoid crashes/timeouts:
+            // https://github.com/GoogleChrome/puppeteer/issues/1834
+            '--disable-dev-shm-usage',
+          ],
         },
     };
   }
diff --git a/modules/canvaskit/karma.conf.js b/modules/canvaskit/karma.conf.js
index e815e04..dd05944 100644
--- a/modules/canvaskit/karma.conf.js
+++ b/modules/canvaskit/karma.conf.js
@@ -62,12 +62,18 @@
     cfg.browsers = ['ChromeHeadlessNoSandbox'],
     cfg.customLaunchers = {
         ChromeHeadlessNoSandbox: {
-            base: 'ChromeHeadless',
-            flags: [
+          base: 'ChromeHeadless',
+          flags: [
             // Without this flag, we see an error:
             // Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted
-                '--no-sandbox'
-            ],
+            '--no-sandbox',
+            // may help tests be less flaky
+            // https://peter.sh/experiments/chromium-command-line-switches/#browser-test
+            '--browser-test',
+            // This can also help avoid crashes/timeouts:
+            // https://github.com/GoogleChrome/puppeteer/issues/1834
+            '--disable-dev-shm-usage',
+          ],
         },
     };
   }
diff --git a/modules/canvaskit/paragraph.js b/modules/canvaskit/paragraph.js
index f342730..84d808e 100644
--- a/modules/canvaskit/paragraph.js
+++ b/modules/canvaskit/paragraph.js
@@ -13,8 +13,14 @@
         return [];
       }
       var ret = [];
-      for (var i = 0; i < floatArray.length; i+=4) {
-        ret.push(CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]))
+      for (var i = 0; i < floatArray.length; i+=5) {
+        var r = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]);
+        if (floatArray[i+4] === 1) {
+          r['direction'] = CanvasKit.TextDirection.RTL;
+        } else {
+          r['direction'] = CanvasKit.TextDirection.LTR;
+        }
+        ret.push(r);
       }
       CanvasKit._free(floatArray.byteOffset);
       return ret;
diff --git a/modules/canvaskit/paragraph_bindings.cpp b/modules/canvaskit/paragraph_bindings.cpp
index dbb5639..13f8c8e 100644
--- a/modules/canvaskit/paragraph_bindings.cpp
+++ b/modules/canvaskit/paragraph_bindings.cpp
@@ -123,21 +123,34 @@
     return ps;
 }
 
+struct SimpleTextBox {
+    SkRect rect;
+    // This isn't the most efficient way to represent this, but it is much easier to keep
+    // everything as floats when unpacking on the JS side.
+    // 0.0 = RTL, 1.0 = LTr
+    SkScalar direction;
+};
+
 Float32Array GetRectsForRange(para::ParagraphImpl& self, unsigned start, unsigned end,
                             para::RectHeightStyle heightStyle, para::RectWidthStyle widthStyle) {
     std::vector<para::TextBox> boxes = self.getRectsForRange(start, end, heightStyle, widthStyle);
-    // Pack these text boxes into an array of n groups of 4 SkScalar (floats)
+    // Pack these text boxes into an array of n groups of 5 SkScalar (floats)
     if (!boxes.size()) {
         return emscripten::val::null();
     }
-    SkRect* rects = new SkRect[boxes.size()];
+    SimpleTextBox* rects = new SimpleTextBox[boxes.size()];
     for (int i = 0; i< boxes.size(); i++) {
-        rects[i] = boxes[i].rect;
+        rects[i].rect = boxes[i].rect;
+        if (boxes[i].direction == para::TextDirection::kRtl) {
+            rects[i].direction = 0;
+        } else {
+            rects[i].direction = 1;
+        }
     }
     float* fPtr = reinterpret_cast<float*>(rects);
     // Of note: now that we have cast rects to float*, emscripten is smart enough to wrap this
     // into a Float32Array for us.
-    return Float32Array(typed_memory_view(boxes.size()*4, fPtr));
+    return Float32Array(typed_memory_view(boxes.size()*5, fPtr));
 }
 
 EMSCRIPTEN_BINDINGS(Paragraph) {
@@ -146,10 +159,19 @@
 
     // This "base<>" tells Emscripten that ParagraphImpl is a Paragraph and can get substituted
     // in properly in drawParagraph. However, Emscripten will not let us bind pure virtual methods
-    // so we have to "expose" the ParagraphImpl and its methods.
+    // so we have to "expose" the ParagraphImpl in those cases.
     class_<para::ParagraphImpl, base<para::Paragraph>>("ParagraphImpl")
-        .function("_getRectsForRange", &GetRectsForRange)
+        .function("didExceedMaxLines", &para::Paragraph::didExceedMaxLines)
+        .function("getAlphabeticBaseline", &para::Paragraph::getAlphabeticBaseline)
         .function("getGlyphPositionAtCoordinate", &para::ParagraphImpl::getGlyphPositionAtCoordinate)
+        .function("getHeight", &para::Paragraph::getHeight)
+        .function("getIdeographicBaseline", &para::Paragraph::getIdeographicBaseline)
+        .function("getLongestLine", &para::Paragraph::getLongestLine)
+        .function("getMaxIntrinsicWidth", &para::Paragraph::getMaxIntrinsicWidth)
+        .function("getMaxWidth", &para::Paragraph::getMaxWidth)
+        .function("getMinIntrinsicWidth", &para::Paragraph::getMinIntrinsicWidth)
+        .function("_getRectsForRange", &GetRectsForRange)
+        .function("getWordBoundary", &para::ParagraphImpl::getWordBoundary)
         .function("layout", &para::ParagraphImpl::layout);
 
     class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
@@ -207,8 +229,11 @@
         .value("UltraExpanded",        SkFontStyle::Width::kUltraExpanded_Width);
 
     enum_<para::RectHeightStyle>("RectHeightStyle")
-        .value("Tight",  para::RectHeightStyle::kTight)
-        .value("Max",    para::RectHeightStyle::kMax);
+        .value("Tight",                     para::RectHeightStyle::kTight)
+        .value("Max",                       para::RectHeightStyle::kMax)
+        .value("IncludeLineSpacingMiddle",  para::RectHeightStyle::kIncludeLineSpacingMiddle)
+        .value("IncludeLineSpacingTop",     para::RectHeightStyle::kIncludeLineSpacingTop)
+        .value("IncludeLineSpacingBottom",  para::RectHeightStyle::kIncludeLineSpacingBottom);
 
     enum_<para::RectWidthStyle>("RectWidthStyle")
         .value("Tight",  para::RectWidthStyle::kTight)
@@ -231,7 +256,7 @@
         .field("pos",      &para::PositionWithAffinity::position)
         .field("affinity", &para::PositionWithAffinity::affinity);
 
- value_object<SimpleFontStyle>("FontStyle")
+    value_object<SimpleFontStyle>("FontStyle")
         .field("slant",     &SimpleFontStyle::slant)
         .field("weight",    &SimpleFontStyle::weight)
         .field("width",     &SimpleFontStyle::width);
@@ -257,6 +282,12 @@
         .field("foregroundColor",     &SimpleTextStyle::foregroundColor)
         .field("_numFontFamilies",    &SimpleTextStyle::numFontFamilies);
 
+    // The U stands for unsigned - we can't bind a generic/template object, so we have to specify it
+    // with the type we are using.
+    value_object<para::SkRange<size_t>>("URange")
+        .field("start",    &para::SkRange<size_t>::start)
+        .field("end",      &para::SkRange<size_t>::end);
+
     // TextDecoration should be a const because they can be combined
     constant("NoDecoration", int(para::TextDecoration::kNoDecoration));
     constant("UnderlineDecoration", int(para::TextDecoration::kUnderline));
diff --git a/modules/canvaskit/particles.js b/modules/canvaskit/particles.js
new file mode 100644
index 0000000..3f59a93
--- /dev/null
+++ b/modules/canvaskit/particles.js
@@ -0,0 +1,54 @@
+// Adds compile-time JS functions to augment the CanvasKit interface.
+// Specifically, anything that should only be on the Particle builds of canvaskit.
+
+// assets is a dictionary of named blobs: { key: ArrayBuffer, ... }
+// The keys should be well-behaved strings - they're turned into null-terminated
+// strings for the native side.
+CanvasKit.MakeParticles = function(json, assets) {
+  if (!CanvasKit._MakeParticles) {
+    throw 'Not compiled with MakeParticles';
+  }
+  if (!assets) {
+    return CanvasKit._MakeParticles(json, 0, nullptr, nullptr, nullptr);
+  }
+  var assetNamePtrs = [];
+  var assetDataPtrs = [];
+  var assetSizes    = [];
+
+  var assetKeys = Object.keys(assets || {});
+  for (var i = 0; i < assetKeys.length; i++) {
+    var key = assetKeys[i];
+    var buffer = assets[key];
+    var data = new Uint8Array(buffer);
+
+    var iptr = CanvasKit._malloc(data.byteLength);
+    CanvasKit.HEAPU8.set(data, iptr);
+    assetDataPtrs.push(iptr);
+    assetSizes.push(data.byteLength);
+
+    // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
+    // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
+    // Add 1 for null terminator
+    var strLen = lengthBytesUTF8(key) + 1;
+    var strPtr = CanvasKit._malloc(strLen);
+
+    stringToUTF8(key, strPtr, strLen);
+    assetNamePtrs.push(strPtr);
+  }
+
+  // Not entirely sure if it matters, but the uintptr_t are 32 bits
+  // we want to copy our array of uintptr_t into the right size memory.
+  var namesPtr      = copy1dArray(assetNamePtrs, CanvasKit.HEAPU32);
+  var assetsPtr     = copy1dArray(assetDataPtrs, CanvasKit.HEAPU32);
+  var assetSizesPtr = copy1dArray(assetSizes,    CanvasKit.HEAPU32);
+
+  var particles = CanvasKit._MakeParticles(json, assetKeys.length,
+                                           namesPtr, assetsPtr, assetSizesPtr);
+
+  // The C++ code has made copies of the asset and string data, so free our copies.
+  CanvasKit._free(namesPtr);
+  CanvasKit._free(assetsPtr);
+  CanvasKit._free(assetSizesPtr);
+
+  return particles;
+};
diff --git a/modules/canvaskit/particles_bindings.cpp b/modules/canvaskit/particles_bindings.cpp
index f64d22a..2aa679b 100644
--- a/modules/canvaskit/particles_bindings.cpp
+++ b/modules/canvaskit/particles_bindings.cpp
@@ -10,6 +10,7 @@
 #include "include/utils/SkRandom.h"
 #include "modules/particles/include/SkParticleEffect.h"
 #include "modules/particles/include/SkParticleSerialization.h"
+#include "modules/skresources/include/SkResources.h"
 
 #include <string>
 
@@ -18,6 +19,61 @@
 
 using namespace emscripten;
 
+namespace {
+
+class ParticleAssetProvider : public skresources::ResourceProvider {
+public:
+    ~ParticleAssetProvider() override = default;
+
+    // Tried using a map, but that gave strange errors like
+    // https://emscripten.org/docs/porting/guidelines/function_pointer_issues.html
+    // Not entirely sure why, but perhaps the iterator in the map was
+    // confusing enscripten.
+    using AssetVec = std::vector<std::pair<SkString, sk_sp<SkData>>>;
+
+    static sk_sp<ParticleAssetProvider> Make(AssetVec assets) {
+        if (assets.empty()) {
+            return nullptr;
+        }
+
+        return sk_sp<ParticleAssetProvider>(new ParticleAssetProvider(std::move(assets)));
+    }
+
+    sk_sp<skresources::ImageAsset> loadImageAsset(const char[] /* path */,
+                                                  const char name[],
+                                                  const char[] /* id */) const override {
+        // For CK we ignore paths & IDs, and identify images based solely on name.
+        if (auto data = this->findAsset(name)) {
+            return skresources::MultiFrameImageAsset::Make(std::move(data));
+        }
+
+        return nullptr;
+    }
+
+    sk_sp<SkData> loadFont(const char name[], const char[] /* url */) const override {
+        // Same as images paths, we ignore font URLs.
+        return this->findAsset(name);
+    }
+
+private:
+    explicit ParticleAssetProvider(AssetVec assets) : fAssets(std::move(assets)) {}
+
+    sk_sp<SkData> findAsset(const char name[]) const {
+        for (const auto& asset : fAssets) {
+            if (asset.first.equals(name)) {
+                return asset.second;
+            }
+        }
+
+        SkDebugf("Could not find %s\n", name);
+        return nullptr;
+    }
+
+    const AssetVec fAssets;
+};
+
+}
+
 EMSCRIPTEN_BINDINGS(Particles) {
     class_<SkParticleEffect>("SkParticleEffect")
         .smart_ptr<sk_sp<SkParticleEffect>>("sk_sp<SkParticleEffect>")
@@ -27,18 +83,42 @@
         .function("setPosition", select_overload<void (SkPoint)>(&SkParticleEffect::setPosition))
         .function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate));
 
-    function("MakeParticles", optional_override([](std::string json)->sk_sp<SkParticleEffect> {
+    function("_MakeParticles", optional_override([](std::string json,
+                                                   size_t assetCount,
+                                                   uintptr_t /* char**    */ nptr,
+                                                   uintptr_t /* uint8_t** */ dptr,
+                                                   uintptr_t /* size_t*   */ sptr)
+                                                ->sk_sp<SkParticleEffect> {
+        // See the comment in canvaskit_bindings.cpp about the use of uintptr_t
         static bool didInit = false;
         if (!didInit) {
             SkParticleEffect::RegisterParticleTypes();
             didInit = true;
         }
+
+        const auto assetNames = reinterpret_cast<char**   >(nptr);
+        const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
+        const auto assetSizes = reinterpret_cast<size_t*  >(sptr);
+
+        ParticleAssetProvider::AssetVec assets;
+        assets.reserve(assetCount);
+
+        for (size_t i = 0; i < assetCount; i++) {
+            auto name  = SkString(assetNames[i]);
+            auto bytes = SkData::MakeFromMalloc(assetDatas[i], assetSizes[i]);
+            assets.push_back(std::make_pair(std::move(name), std::move(bytes)));
+        }
+
         SkRandom r;
         sk_sp<SkParticleEffectParams> params(new SkParticleEffectParams());
         skjson::DOM dom(json.c_str(), json.length());
         SkFromJsonVisitor fromJson(dom.root());
         params->visitFields(&fromJson);
-        return sk_sp<SkParticleEffect>(new SkParticleEffect(std::move(params), r));
+        return sk_sp<SkParticleEffect>(new SkParticleEffect(
+                std::move(params),
+                skresources::DataURIResourceProviderProxy::Make(
+                    ParticleAssetProvider::Make(std::move(assets))),
+                r));
     }));
     constant("particles", true);
 
diff --git a/modules/canvaskit/skottie.js b/modules/canvaskit/skottie.js
index 33e691a..0bd6be9 100644
--- a/modules/canvaskit/skottie.js
+++ b/modules/canvaskit/skottie.js
@@ -1,7 +1,9 @@
 // Adds compile-time JS functions to augment the CanvasKit interface.
 // Specifically, anything that should only be on the Skottie builds of canvaskit.
 
-
+// assets is a dictionary of named blobs: { key: ArrayBuffer, ... }
+// The keys should be well-behaved strings - they're turned into null-terminated
+// strings for the native side.
 CanvasKit.MakeManagedAnimation = function(json, assets) {
   if (!CanvasKit._MakeManagedAnimation) {
     throw 'Not compiled with MakeManagedAnimation';
@@ -43,8 +45,7 @@
   var anim = CanvasKit._MakeManagedAnimation(json, assetKeys.length, namesPtr,
                                              assetsPtr, assetSizesPtr);
 
-  // We leave the asset data arrays and string data live and assume
-  // it is now owned by the C++ code
+  // The C++ code has made copies of the asset and string data, so free our copies.
   CanvasKit._free(namesPtr);
   CanvasKit._free(assetsPtr);
   CanvasKit._free(assetSizesPtr);
diff --git a/modules/canvaskit/skottie_bindings.cpp b/modules/canvaskit/skottie_bindings.cpp
index d11b233..7d54914 100644
--- a/modules/canvaskit/skottie_bindings.cpp
+++ b/modules/canvaskit/skottie_bindings.cpp
@@ -23,6 +23,7 @@
 #if SK_INCLUDE_MANAGED_SKOTTIE
 #include "modules/skottie/include/SkottieProperty.h"
 #include "modules/skottie/utils/SkottieUtils.h"
+#include "modules/skresources/include/SkResources.h"
 #endif // SK_INCLUDE_MANAGED_SKOTTIE
 
 using namespace emscripten;
@@ -53,7 +54,7 @@
                                               const char[] /* id */) const override {
         // For CK/Skottie we ignore paths & IDs, and identify images based solely on name.
         if (auto data = this->findAsset(name)) {
-            return skottie_utils::MultiFrameImageAsset::Make(std::move(data), true /* predecode */);
+            return skresources::MultiFrameImageAsset::Make(std::move(data));
         }
 
         return nullptr;
@@ -232,6 +233,7 @@
                                                            uintptr_t /* uint8_t**  */ dptr,
                                                            uintptr_t /* size_t*    */ sptr)
                                                         ->sk_sp<ManagedAnimation> {
+        // See the comment in canvaskit_bindings.cpp about the use of uintptr_t
         const auto assetNames = reinterpret_cast<char**   >(nptr);
         const auto assetDatas = reinterpret_cast<uint8_t**>(dptr);
         const auto assetSizes = reinterpret_cast<size_t*  >(sptr);
@@ -246,9 +248,8 @@
         }
 
         return ManagedAnimation::Make(json,
-                 skottie_utils::DataURIResourceProviderProxy::Make(
-                    SkottieAssetProvider::Make(std::move(assets)),
-                    /*predecode=*/true));
+                 skresources::DataURIResourceProviderProxy::Make(
+                    SkottieAssetProvider::Make(std::move(assets))));
     }));
     constant("managed_skottie", true);
 #endif // SK_INCLUDE_MANAGED_SKOTTIE
diff --git a/modules/canvaskit/tests/canvas.spec.js b/modules/canvaskit/tests/canvas.spec.js
index 914bdbf..28dadef 100644
--- a/modules/canvaskit/tests/canvas.spec.js
+++ b/modules/canvaskit/tests/canvas.spec.js
@@ -394,4 +394,124 @@
         }));
     });
 
+    it('can save layer with SaveLayerRec-like things', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            // Note: fiddle.skia.org quietly draws a white background before doing
+            // other things, which is noticed in cases like this where we use saveLayer
+            // with the rec struct.
+            canvas.clear(CanvasKit.WHITE);
+            canvas.scale(8, 8);
+            const redPaint = new CanvasKit.SkPaint();
+            redPaint.setColor(CanvasKit.RED);
+            redPaint.setAntiAlias(true);
+            canvas.drawCircle(21, 21, 8, redPaint);
+
+            const bluePaint = new CanvasKit.SkPaint();
+            bluePaint.setColor(CanvasKit.BLUE);
+            canvas.drawCircle(31, 21, 8, bluePaint);
+
+            const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
+
+            const count = canvas.saveLayer(null, blurIF, 0);
+            expect(count).toEqual(1);
+            canvas.scale(1/4, 1/4);
+            canvas.drawCircle(125, 85, 8, redPaint);
+            canvas.restore();
+
+            surface.flush();
+            blurIF.delete();
+            redPaint.delete();
+            bluePaint.delete();
+
+            reportSurface(surface, 'savelayerrec_canvas', done);
+        }));
+    });
+
+    it('can drawPoints', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const paint = new CanvasKit.SkPaint();
+            paint.setAntiAlias(true);
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+            paint.setStrokeWidth(10);
+            paint.setColor(CanvasKit.Color(153, 204, 162, 0.82));
+
+            const points = [[32, 16], [48, 48], [16, 32]];
+
+            const caps = [CanvasKit.StrokeCap.Round, CanvasKit.StrokeCap.Square,
+                          CanvasKit.StrokeCap.Butt];
+            const joins = [CanvasKit.StrokeJoin.Round, CanvasKit.StrokeJoin.Miter,
+                           CanvasKit.StrokeJoin.Bevel];
+            const modes = [CanvasKit.PointMode.Points, CanvasKit.PointMode.Lines,
+                           CanvasKit.PointMode.Polygon];
+
+            for (let i = 0; i < caps.length; i++) {
+                paint.setStrokeCap(caps[i]);
+                paint.setStrokeJoin(joins[i]);
+
+                for (const m of modes) {
+                    canvas.drawPoints(m, points, paint);
+                    canvas.translate(64, 0);
+                }
+                // Try with the malloc approach. Note that the drawPoints
+                // will free the pointer when done.
+                const mPoints = CanvasKit.Malloc(Float32Array, 3*2);
+                mPoints.set([32, 16, 48, 48, 16, 32]);
+                canvas.drawPoints(CanvasKit.PointMode.Polygon, mPoints, paint);
+                canvas.translate(-192, 64);
+            }
+
+            surface.flush();
+            paint.delete();
+
+            reportSurface(surface, 'drawpoints_canvas', done);
+        }));
+    });
+
+    it('can stretch an image with drawImageNine', function(done) {
+        const imgPromise = fetch('/assets/mandrill_512.png')
+            .then((response) => response.arrayBuffer());
+        Promise.all([imgPromise, LoadCanvasKit]).then((values) => {
+            const pngData = values[0];
+            expect(pngData).toBeTruthy();
+            catchException(done, () => {
+                let img = CanvasKit.MakeImageFromEncoded(pngData);
+                expect(img).toBeTruthy();
+                const surface = CanvasKit.MakeCanvasSurface('test');
+                expect(surface).toBeTruthy('Could not make surface')
+                if (!surface) {
+                    done();
+                    return;
+                }
+                const canvas = surface.getCanvas();
+                canvas.clear(CanvasKit.WHITE);
+                const paint = new CanvasKit.SkPaint();
+
+                canvas.drawImageNine(img, {
+                    fLeft: 40,
+                    fTop: 40,
+                    fRight: 400,
+                    fBottom: 300,
+                }, CanvasKit.LTRBRect(5, 5, 300, 650), paint);
+                surface.flush();
+                paint.delete();
+                img.delete();
+
+                reportSurface(surface, 'drawImageNine_canvas', done);
+            })();
+        });
+    });
 });
diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js
index f68016d..481c4bb 100644
--- a/modules/canvaskit/tests/core.spec.js
+++ b/modules/canvaskit/tests/core.spec.js
@@ -157,6 +157,9 @@
                 let aImg = CanvasKit.MakeAnimatedImageFromEncoded(gifData);
                 expect(aImg).toBeTruthy();
                 expect(aImg.getRepetitionCount()).toEqual(-1); // infinite loop
+                expect(aImg.width()).toEqual(320);
+                expect(aImg.height()).toEqual(240);
+                expect(aImg.getFrameCount()).toEqual(60);
 
                 const surface = CanvasKit.MakeCanvasSurface('test');
                 expect(surface).toBeTruthy('Could not make surface')
@@ -188,4 +191,111 @@
         });
     });
 
+    it('can blur using ImageFilter or MaskFilter', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const pathUL = starPath(CanvasKit, 100, 100, 80);
+            const pathBR = starPath(CanvasKit, 400, 300, 80);
+            const paint = new CanvasKit.SkPaint();
+            const textFont = new CanvasKit.SkFont(null, 24);
+
+            canvas.drawText('Above: MaskFilter', 20, 220, paint, textFont);
+            canvas.drawText('Right: ImageFilter', 20, 260, paint, textFont);
+
+            paint.setColor(CanvasKit.BLUE);
+
+            const blurMask = CanvasKit.SkMaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, 5, true);
+            paint.setMaskFilter(blurMask);
+            canvas.drawPath(pathUL, paint);
+
+            const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 1, CanvasKit.TileMode.Decal, null);
+            paint.setImageFilter(blurIF);
+            canvas.drawPath(pathBR, paint);
+
+            surface.flush();
+
+            pathUL.delete();
+            pathBR.delete();
+            paint.delete();
+            blurMask.delete();
+            blurIF.delete();
+            textFont.delete();
+
+            reportSurface(surface, 'blur_filters', done);
+        }));
+    });
+
+    it('can use various image filters', function(done) {
+        const imgPromise = fetch('/assets/mandrill_512.png')
+            .then((response) => response.arrayBuffer());
+        Promise.all([imgPromise, LoadCanvasKit]).then((values) => {
+            const pngData = values[0];
+            expect(pngData).toBeTruthy();
+            catchException(done, () => {
+                let img = CanvasKit.MakeImageFromEncoded(pngData);
+                expect(img).toBeTruthy();
+                const surface = CanvasKit.MakeCanvasSurface('test');
+                expect(surface).toBeTruthy('Could not make surface')
+                if (!surface) {
+                    done();
+                    return;
+                }
+                const canvas = surface.getCanvas();
+                canvas.clear(CanvasKit.WHITE);
+                const paint = new CanvasKit.SkPaint();
+                paint.setAntiAlias(true);
+                paint.setColor(CanvasKit.Color(0, 255, 0, 1.0));
+                const redCF =  CanvasKit.SkColorFilter.MakeBlend(
+                        CanvasKit.Color(255, 0, 0, 0.1), CanvasKit.BlendMode.SrcOver);
+                const redIF = CanvasKit.SkImageFilter.MakeColorFilter(redCF, null);
+                const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
+                const combined = CanvasKit.SkImageFilter.MakeCompose(redIF, blurIF);
+
+                // rotate 10 degrees centered on 200, 200
+                const m = CanvasKit.SkMatrix.rotated(Math.PI/18, 200, 200);
+                const rotated = CanvasKit.SkImageFilter.MakeMatrixTransform(m, CanvasKit.FilterQuality.Medium, combined);
+                paint.setImageFilter(rotated);
+
+                //canvas.rotate(10, 200, 200);
+                canvas.drawImage(img, 0, 0, paint);
+                canvas.drawRect(CanvasKit.LTRBRect(5, 35, 45, 80), paint);
+
+                surface.flush();
+
+                paint.delete();
+                redIF.delete();
+                redCF.delete();
+                blurIF.delete();
+                combined.delete();
+                rotated.delete();
+                img.delete();
+
+                reportSurface(surface, 'combined_filters', done);
+            })();
+        });
+    });
+
+    it('can use DecodeCache APIs', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            const initialLimit = CanvasKit.getDecodeCacheLimitBytes();
+            expect(initialLimit).toBeGreaterThan(1024 * 1024);
+
+            const newLimit = 42 * 1024 * 1024;
+            CanvasKit.setDecodeCacheLimitBytes(newLimit);
+            expect(CanvasKit.getDecodeCacheLimitBytes()).toEqual(newLimit);
+
+            // We cannot make any assumptions about this value,
+            // so we just make sure it doesn't crash.
+            CanvasKit.getDecodeCacheUsedBytes();
+
+            done();
+        }));
+    });
+
 });
diff --git a/modules/canvaskit/tests/paragraph.spec.js b/modules/canvaskit/tests/paragraph.spec.js
index d9f7500..6c695ee 100644
--- a/modules/canvaskit/tests/paragraph.spec.js
+++ b/modules/canvaskit/tests/paragraph.spec.js
@@ -85,6 +85,23 @@
 
             paragraph.layout(wrapTo);
 
+            expect(paragraph.didExceedMaxLines()).toBeTruthy();
+            expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
+            expect(paragraph.getHeight()).toEqual(240);
+            expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
+            expect(paragraph.getLongestLine()).toBeCloseTo(142.129, 3);
+            expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1444.250, 3);
+            expect(paragraph.getMaxWidth()).toEqual(200);
+            expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
+            expect(paragraph.getWordBoundary(8)).toEqual({
+                start: 0,
+                end: 14,
+            });
+            expect(paragraph.getWordBoundary(25)).toEqual({
+                start: 25,
+                end: 26,
+            });
+
             canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
             canvas.drawParagraph(paragraph, 10, 10);
 
@@ -175,6 +192,7 @@
                 expect(rects.length).toEqual(test.expectedNum);
 
                 for (const rect of rects) {
+                    expect(rect.direction).toEqual(CanvasKit.TextDirection.LTR);
                     const p = new CanvasKit.SkPaint();
                     p.setColor(test.color);
                     p.setStyle(CanvasKit.PaintStyle.Stroke);
@@ -189,8 +207,7 @@
         }));
     });
 
-    // Disabled until we can update CanvasKit's freetype.
-    xit('can draw emojis', function(done) {
+    it('can draw emojis', function(done) {
         Promise.all([LoadCanvasKit, notoSerifFontLoaded, emojiFontLoaded]).then(catchException(done, () => {
             const surface = CanvasKit.MakeCanvasSurface('test');
             expect(surface).toBeTruthy('Could not make surface')
@@ -201,25 +218,34 @@
             const canvas = surface.getCanvas();
 
             const fontMgr = CanvasKit.SkFontMgr.FromData([notoSerifFontBuffer, emojiFontBuffer]);
-            fontMgr.dumpFamilies();
+            if (fontMgr.dumpFamilies) {
+                fontMgr.dumpFamilies();
+            }
 
             const wrapTo = 450;
 
             const paraStyle = new CanvasKit.ParagraphStyle({
                 textStyle: {
                     color: CanvasKit.BLACK,
-                    // Put emoji first, otherwise zero-space-joiner will be matched by serif,
-                    // and we don't get families or rainbow flags.
-                    fontFamilies: ['Noto Color Emoji', 'Noto Serif'],
+                    // Put text first, otherwise the "emoji space" is used and that looks bad.
+                    fontFamilies: ['Noto Serif', 'Noto Color Emoji'],
                     fontSize: 30,
                 },
                 textAlign: CanvasKit.TextAlign.Left,
                 maxLines: 10,
             });
+
+            const textStyle = new CanvasKit.TextStyle({
+                color: CanvasKit.BLACK,
+                    // The number 4 matches an emoji and looks strange w/o this additional style.
+                    fontFamilies: ['Noto Serif'],
+                    fontSize: 30,
+            });
+
             const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
-            // FIXME(kjlubick): We need one style that doesn't have emoji, otherwise the 4 will
-            // be "emoji 4".
+            builder.pushStyle(textStyle);
             builder.addText('4 flags on following line:\n');
+            builder.pop();
             builder.addText(`🏳️‍🌈 🇮🇹 🇱🇷 🇺🇸\n`);
             builder.addText('Rainbow Italy Liberia USA\n\n');
             builder.addText('Emoji below should wrap:\n');
@@ -230,8 +256,16 @@
 
             canvas.drawParagraph(paragraph, 10, 10);
 
+            const paint = new CanvasKit.SkPaint();
+            paint.setColor(CanvasKit.RED);
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+            canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
+
             surface.flush();
             fontMgr.delete();
+            paint.delete();
+            builder.delete();
+
             reportSurface(surface, 'paragraph_emoji', done);
         }));
     });
@@ -364,4 +398,117 @@
         }));
     });
 
+    it('should not crash if we omit font family on pushed textStyle', function(done) {
+        Promise.all([LoadCanvasKit, notoSerifFontLoaded, notoSerifBoldItalicFontLoaded]).then(catchException(done, () => {
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const paint = new CanvasKit.SkPaint();
+
+            paint.setColor(CanvasKit.RED);
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+
+            const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
+
+            const wrapTo = 250;
+
+            const paraStyle = new CanvasKit.ParagraphStyle({
+                textStyle: {
+                    fontFamilies: ['Noto Serif'],
+                    fontSize: 20,
+                },
+                textDirection: CanvasKit.TextDirection.RTL,
+                disableHinting: true,
+            });
+
+            const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
+            builder.addText('Default text\n');
+
+            const boldItalic = new CanvasKit.TextStyle({
+                fontStyle: {
+                    weight: CanvasKit.FontWeight.Bold,
+                    slant: CanvasKit.FontSlant.Italic,
+                }
+            });
+            builder.pushStyle(boldItalic)
+            builder.addText(`Bold, Italic\n`); // doesn't show up, but we don't crash
+            builder.pop();
+            builder.addText(`back to normal`);
+            const paragraph = builder.build();
+
+            paragraph.layout(wrapTo);
+
+            canvas.clear(CanvasKit.Color(250, 250, 250));
+            canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
+            canvas.drawParagraph(paragraph, 10, 10);
+
+            surface.flush();
+
+            paragraph.delete();
+            paint.delete();
+            fontMgr.delete();
+            done();
+        }));
+    });
+
+    it('should not crash if we omit font family on paragraph style', function(done) {
+        Promise.all([LoadCanvasKit, notoSerifFontLoaded, notoSerifBoldItalicFontLoaded]).then(catchException(done, () => {
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const paint = new CanvasKit.SkPaint();
+
+            paint.setColor(CanvasKit.RED);
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+
+            const fontMgr = CanvasKit.SkFontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
+
+            const wrapTo = 250;
+
+            const paraStyle = new CanvasKit.ParagraphStyle({
+                textStyle: {
+                    fontSize: 20,
+                },
+                textDirection: CanvasKit.TextDirection.RTL,
+                disableHinting: true,
+            });
+
+            const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
+            builder.addText('Default text\n');
+
+            const boldItalic = new CanvasKit.TextStyle({
+                fontStyle: {
+                    weight: CanvasKit.FontWeight.Bold,
+                    slant: CanvasKit.FontSlant.Italic,
+                }
+            });
+            builder.pushStyle(boldItalic)
+            builder.addText(`Bold, Italic\n`);
+            builder.pop();
+            builder.addText(`back to normal`);
+            const paragraph = builder.build();
+
+            paragraph.layout(wrapTo);
+
+            canvas.clear(CanvasKit.Color(250, 250, 250));
+            canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
+            canvas.drawParagraph(paragraph, 10, 10);
+
+            surface.flush();
+
+            paragraph.delete();
+            paint.delete();
+            fontMgr.delete();
+            done();
+        }));
+    });
+
 });
diff --git a/modules/canvaskit/tests/path.spec.js b/modules/canvaskit/tests/path.spec.js
index 66555fc..8c8b598 100644
--- a/modules/canvaskit/tests/path.spec.js
+++ b/modules/canvaskit/tests/path.spec.js
@@ -216,4 +216,154 @@
             reportSurface(surface, 'arcto_path', done);
         }));
     });
+
+    it('can draw a path using relative functions', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            // This is taken from example.html
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const paint = new CanvasKit.SkPaint();
+            paint.setStrokeWidth(1.0);
+            paint.setAntiAlias(true);
+            paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+
+            const path = new CanvasKit.SkPath();
+            path.rMoveTo(20, 5)
+                .rLineTo(10, 15)  // 30, 20
+                .rLineTo(10, -5);  // 40, 10
+            path.rLineTo(10, 10);  // 50, 20
+            path.rLineTo(10, -20); // 60, 0
+            path.rLineTo(-40, 5);  // 20, 5
+
+            path.moveTo(20, 80)
+                .rCubicTo(70, -70, 140, 70, 170, -70); // 90, 10, 160, 150, 190, 10
+
+            path.moveTo(36, 148)
+                .rQuadTo(30, 40, 84, -12) // 66, 188, 120, 136
+                .lineTo(36, 148);
+
+            path.moveTo(150, 180)
+                .rArcTo(24, 24, 45, true, false, -68, -24); // 82, 156
+            path.lineTo(160, 160);
+
+            canvas.drawPath(path, paint);
+
+            surface.flush();
+            path.delete();
+            paint.delete();
+
+            reportSurface(surface, 'path_relative', done);
+        }));
+    });
+
+    it('can measure a path', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+
+            const path = new CanvasKit.SkPath();
+            path.moveTo(10, 10)
+                .lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule
+
+            path.moveTo(80, 0)
+                .lineTo(80, 10)
+                .lineTo(100, 5)
+                .lineTo(80, 0);
+
+            const meas = new CanvasKit.SkPathMeasure(path, false, 1);
+            expect(meas.getLength()).toBeCloseTo(50.0, 3);
+            const pt = meas.getPosTan(28.7); // arbitrary point
+            expect(pt[0]).toBeCloseTo(27.22, 3); // x
+            expect(pt[1]).toBeCloseTo(32.96, 3); // y
+            expect(pt[2]).toBeCloseTo(0.6, 3);   // dy
+            expect(pt[3]).toBeCloseTo(0.8, 3);   // dy
+            const subpath = meas.getSegment(20, 40, true); // make sure this doesn't crash
+
+            expect(meas.nextContour()).toBeTruthy();
+            expect(meas.getLength()).toBeCloseTo(51.231, 3);
+
+            expect(meas.nextContour()).toBeFalsy();
+
+            path.delete();
+            done();
+        }));
+    });
+
+    it('can measure the contours of a path', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+
+            const path = new CanvasKit.SkPath();
+            path.moveTo(10, 10)
+                .lineTo(40, 50); // should be length 50 because of the 3/4/5 triangle rule
+
+            path.moveTo(80, 0)
+                .lineTo(80, 10)
+                .lineTo(100, 5)
+                .lineTo(80, 0);
+
+            const meas = new CanvasKit.SkContourMeasureIter(path, false, 1);
+            let cont = meas.next();
+            expect(cont).toBeTruthy();
+
+            expect(cont.length()).toBeCloseTo(50.0, 3);
+            const pt = cont.getPosTan(28.7); // arbitrary point
+            expect(pt[0]).toBeCloseTo(27.22, 3); // x
+            expect(pt[1]).toBeCloseTo(32.96, 3); // y
+            expect(pt[2]).toBeCloseTo(0.6, 3);   // dy
+            expect(pt[3]).toBeCloseTo(0.8, 3);   // dy
+            const subpath = cont.getSegment(20, 40, true); // make sure this doesn't crash
+
+            cont.delete();
+            cont = meas.next();
+            expect(cont).toBeTruthy()
+            expect(cont.length()).toBeCloseTo(51.231, 3);
+
+            cont.delete();
+            expect(meas.next()).toBeFalsy();
+
+            meas.delete();
+            path.delete();
+            done();
+        }));
+    });
+
+    it('can draw a polygon', function(done) {
+        LoadCanvasKit.then(catchException(done, () => {
+            // This is taken from example.html
+            const surface = CanvasKit.MakeCanvasSurface('test');
+            expect(surface).toBeTruthy('Could not make surface')
+            if (!surface) {
+                done();
+                return;
+            }
+            const canvas = surface.getCanvas();
+            const paint = new CanvasKit.SkPaint();
+            paint.setStrokeWidth(1.0);
+            paint.setAntiAlias(true);
+            paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
+            paint.setStyle(CanvasKit.PaintStyle.Stroke);
+
+            const points = [[5, 5], [30, 20], [55, 5], [55, 50], [30, 30], [5, 50]];
+
+            const mPoints = CanvasKit.Malloc(Float32Array, 6 * 2);
+            mPoints.set([105, 105, 130, 120, 155, 105, 155, 150, 130, 130, 105, 150]);
+
+            const path = new CanvasKit.SkPath();
+            path.addPoly(points, true)
+                .moveTo(100, 0)
+                .addPoly(mPoints, true);
+
+            canvas.drawPath(path, paint);
+
+            surface.flush();
+            path.delete();
+            paint.delete();
+
+            reportSurface(surface, 'drawpoly_path', done);
+        }));
+    });
 });
diff --git a/modules/canvaskit/tests/skottie.spec.js b/modules/canvaskit/tests/skottie.spec.js
index 91cbd0a..ee729c9 100644
--- a/modules/canvaskit/tests/skottie.spec.js
+++ b/modules/canvaskit/tests/skottie.spec.js
@@ -58,6 +58,7 @@
                 canvas.clear(CanvasKit.WHITE);
                 animation.render(canvas, bounds);
                 surface.flush();
+                animation.delete();
 
                 reportSurface(surface, 'skottie_animgif', done);
             })();
diff --git a/modules/canvaskit/tests/util.js b/modules/canvaskit/tests/util.js
index 458602c..2183282 100644
--- a/modules/canvaskit/tests/util.js
+++ b/modules/canvaskit/tests/util.js
@@ -11,7 +11,7 @@
     pixels = new Uint8ClampedArray(pixels.buffer);
     const imageData = new ImageData(pixels, CANVAS_WIDTH, CANVAS_HEIGHT);
 
-    let reportingCanvas =  document.getElementById('report');
+    const reportingCanvas = document.getElementById('report');
     reportingCanvas.getContext('2d').putImageData(imageData, 0, 0);
     reportCanvas(reportingCanvas, testname).then(() => {
         done();
@@ -27,4 +27,4 @@
       p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
     }
     return p;
-  }
\ No newline at end of file
+}
diff --git a/modules/particles/BUILD.gn b/modules/particles/BUILD.gn
index 1573b72..1ce078d 100644
--- a/modules/particles/BUILD.gn
+++ b/modules/particles/BUILD.gn
@@ -22,6 +22,7 @@
     include_dirs = [ "../../tools/timer" ]
     deps = [
       "../..:skia",
+      "../skresources",
     ]
     sources = skia_particle_sources
     configs += [ "../../:skia_private" ]
diff --git a/modules/particles/include/SkParticleDrawable.h b/modules/particles/include/SkParticleDrawable.h
index 94cfdab..fda784f 100644
--- a/modules/particles/include/SkParticleDrawable.h
+++ b/modules/particles/include/SkParticleDrawable.h
@@ -15,17 +15,20 @@
 class SkPaint;
 class SkString;
 
+namespace skresources { class ResourceProvider; }
+
 class SkParticleDrawable : public SkReflected {
 public:
     REFLECTED_ABSTRACT(SkParticleDrawable, SkReflected)
 
-    virtual void draw(SkCanvas* canvas, const SkParticles& particles, int count,
-                      const SkPaint& paint) = 0;
+    virtual void draw(const skresources::ResourceProvider* resourceProvider, SkCanvas* canvas,
+                      const SkParticles& particles, int count, const SkPaint& paint) = 0;
 
     static void RegisterDrawableTypes();
 
     static sk_sp<SkParticleDrawable> MakeCircle(int radius);
-    static sk_sp<SkParticleDrawable> MakeImage(const SkString& path, int cols, int rows);
+    static sk_sp<SkParticleDrawable> MakeImage(const SkString& path, const SkString& name,
+                                               int cols, int rows);
 };
 
 #endif // SkParticleEffect_DEFINED
diff --git a/modules/particles/include/SkParticleEffect.h b/modules/particles/include/SkParticleEffect.h
index fe26408..242dc1c 100644
--- a/modules/particles/include/SkParticleEffect.h
+++ b/modules/particles/include/SkParticleEffect.h
@@ -25,6 +25,10 @@
 class SkParticleDrawable;
 class SkParticleExternalValue;
 
+namespace skresources {
+    class ResourceProvider;
+}
+
 namespace SkSL {
     class ByteCode;
 }
@@ -128,7 +132,8 @@
 
 class SkParticleEffect : public SkRefCnt {
 public:
-    SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random);
+    SkParticleEffect(sk_sp<SkParticleEffectParams> params,
+                     sk_sp<skresources::ResourceProvider> rp, const SkRandom& random);
 
     // Start playing this effect, specifying initial values for the emitter's properties
     void start(double now, bool looping, SkPoint position, SkVector heading, float scale,
@@ -198,7 +203,8 @@
     void processParticleSpawnRequests(double now, int start);
     void runParticleScript(double now, const char* entry, int start, int count);
 
-    sk_sp<SkParticleEffectParams> fParams;
+    sk_sp<SkParticleEffectParams>        fParams;
+    sk_sp<skresources::ResourceProvider> fResourceProvider;
 
     SkRandom fRandom;
 
diff --git a/modules/particles/src/SkParticleDrawable.cpp b/modules/particles/src/SkParticleDrawable.cpp
index 1e67db9..0d5766b 100644
--- a/modules/particles/src/SkParticleDrawable.cpp
+++ b/modules/particles/src/SkParticleDrawable.cpp
@@ -15,6 +15,7 @@
 #include "include/core/SkString.h"
 #include "include/core/SkSurface.h"
 #include "modules/particles/include/SkParticleData.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/core/SkAutoMalloc.h"
 
 static sk_sp<SkImage> make_circle_image(int radius) {
@@ -78,8 +79,8 @@
 
     REFLECTED(SkCircleDrawable, SkParticleDrawable)
 
-    void draw(SkCanvas* canvas, const SkParticles& particles, int count,
-              const SkPaint& paint) override {
+    void draw(const skresources::ResourceProvider* /* unused */, SkCanvas* canvas,
+              const SkParticles& particles, int count, const SkPaint& paint) override {
         SkPoint center = { SkIntToScalar(fRadius), SkIntToScalar(fRadius) };
         DrawAtlasArrays arrays(particles, count, center);
         for (int i = 0; i < count; ++i) {
@@ -110,18 +111,22 @@
 
 class SkImageDrawable : public SkParticleDrawable {
 public:
-    SkImageDrawable(const SkString& path = SkString(), int cols = 1, int rows = 1)
+    SkImageDrawable(const SkString& path = SkString(), const SkString& name = SkString(),
+                    int cols = 1, int rows = 1)
             : fPath(path)
+            , fName(name)
             , fCols(cols)
-            , fRows(rows) {
-        this->rebuild();
-    }
+            , fRows(rows)
+            , fDirty(true) {}
 
     REFLECTED(SkImageDrawable, SkParticleDrawable)
 
-    void draw(SkCanvas* canvas, const SkParticles& particles, int count,
-              const SkPaint& paint) override {
-        SkRect baseRect = getBaseRect();
+    void draw(const skresources::ResourceProvider* resourceProvider, SkCanvas* canvas,
+              const SkParticles& particles, int count, const SkPaint& paint) override {
+        this->rebuildIfDirty(resourceProvider);
+
+        SkRect baseRect = SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
+                                         static_cast<float>(fImage->height()) / fRows);
         SkPoint center = { baseRect.width() * 0.5f, baseRect.height() * 0.5f };
         DrawAtlasArrays arrays(particles, count, center);
 
@@ -139,41 +144,46 @@
     }
 
     void visitFields(SkFieldVisitor* v) override {
-        SkString oldPath = fPath;
+        SkString oldPath = fPath,
+                 oldName = fName;
 
         v->visit("Path", fPath);
+        v->visit("Name", fName);
         v->visit("Columns", fCols);
         v->visit("Rows", fRows);
 
         fCols = SkTMax(fCols, 1);
         fRows = SkTMax(fRows, 1);
-        if (oldPath != fPath) {
-            this->rebuild();
+        if (oldPath != fPath || oldName != fName) {
+            fDirty = true;
         }
     }
 
 private:
     SkString fPath;
+    SkString fName;
     int      fCols;
     int      fRows;
 
-    SkRect getBaseRect() const {
-        return SkRect::MakeWH(static_cast<float>(fImage->width()) / fCols,
-                              static_cast<float>(fImage->height() / fRows));
-    }
+    void rebuildIfDirty(const skresources::ResourceProvider* resourceProvider) {
+        if (!fDirty) {
+            return;
+        }
 
-    void rebuild() {
-        fImage = SkImage::MakeFromEncoded(SkData::MakeFromFileName(fPath.c_str()));
+        fImage.reset();
+        if (auto asset = resourceProvider->loadImageAsset(fPath.c_str(), fName.c_str(), nullptr)) {
+            fImage = asset->getFrame(0);
+        }
         if (!fImage) {
-            if (!fPath.isEmpty()) {
-                SkDebugf("Could not load image \"%s\"\n", fPath.c_str());
-            }
+            SkDebugf("Could not load image \"%s:%s\"\n", fPath.c_str(), fName.c_str());
             fImage = make_circle_image(1);
         }
+        fDirty = false;
     }
 
     // Cached
     sk_sp<SkImage> fImage;
+    bool           fDirty;
 };
 
 void SkParticleDrawable::RegisterDrawableTypes() {
@@ -186,6 +196,7 @@
     return sk_sp<SkParticleDrawable>(new SkCircleDrawable(radius));
 }
 
-sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const SkString& path, int cols, int rows) {
-    return sk_sp<SkParticleDrawable>(new SkImageDrawable(path, cols, rows));
+sk_sp<SkParticleDrawable> SkParticleDrawable::MakeImage(const SkString& path, const SkString& name,
+                                                        int cols, int rows) {
+    return sk_sp<SkParticleDrawable>(new SkImageDrawable(path, name, cols, rows));
 }
diff --git a/modules/particles/src/SkParticleEffect.cpp b/modules/particles/src/SkParticleEffect.cpp
index d78ede5..105dcda 100644
--- a/modules/particles/src/SkParticleEffect.cpp
+++ b/modules/particles/src/SkParticleEffect.cpp
@@ -11,6 +11,7 @@
 #include "modules/particles/include/SkParticleBinding.h"
 #include "modules/particles/include/SkParticleDrawable.h"
 #include "modules/particles/include/SkReflected.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/core/SkMakeUnique.h"
 #include "src/sksl/SkSLByteCode.h"
 #include "src/sksl/SkSLCompiler.h"
@@ -168,8 +169,11 @@
     buildProgram(particleCode, &fParticleProgram);
 }
 
-SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
+SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params,
+                                   sk_sp<skresources::ResourceProvider> rp,
+                                   const SkRandom& random)
         : fParams(std::move(params))
+        , fResourceProvider(std::move(rp))
         , fRandom(random)
         , fLooping(false)
         , fCount(0)
@@ -214,7 +218,7 @@
 void SkParticleEffect::processEffectSpawnRequests(double now) {
     for (const auto& spawnReq : fSpawnRequests) {
         sk_sp<SkParticleEffect> newEffect(new SkParticleEffect(std::move(spawnReq.fParams),
-                                                               fRandom));
+                                                               fResourceProvider, fRandom));
         fRandom.nextU();
 
         newEffect->start(now, spawnReq.fLoop, fState.fPosition, fState.fHeading, fState.fScale,
@@ -245,6 +249,7 @@
     for (const auto& spawnReq : fSpawnRequests) {
         int idx = start + spawnReq.fIndex;
         sk_sp<SkParticleEffect> newEffect(new SkParticleEffect(std::move(spawnReq.fParams),
+                                                               fResourceProvider,
                                                                fParticles.fRandom[idx]));
         newEffect->start(now, spawnReq.fLoop,
                          { data[SkParticles::kPositionX      ][idx],
@@ -467,7 +472,7 @@
     if (this->isAlive(false) && fParams->fDrawable) {
         SkPaint paint;
         paint.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
-        fParams->fDrawable->draw(canvas, fParticles, fCount, paint);
+        fParams->fDrawable->draw(fResourceProvider.get(), canvas, fParticles, fCount, paint);
     }
 
     for (const auto& subEffect : fSubEffects) {
diff --git a/modules/pathkit/karma.bench.conf.js b/modules/pathkit/karma.bench.conf.js
index 315903b..73650d3 100644
--- a/modules/pathkit/karma.bench.conf.js
+++ b/modules/pathkit/karma.bench.conf.js
@@ -59,12 +59,18 @@
     cfg.browsers = ['ChromeHeadlessNoSandbox'],
     cfg.customLaunchers = {
         ChromeHeadlessNoSandbox: {
-            base: 'ChromeHeadless',
-            flags: [
+          base: 'ChromeHeadless',
+          flags: [
             // Without this flag, we see an error:
             // Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted
-                '--no-sandbox'
-            ],
+            '--no-sandbox',
+            // may help tests be less flaky
+            // https://peter.sh/experiments/chromium-command-line-switches/#browser-test
+            '--browser-test',
+            // This can also help avoid crashes/timeouts:
+            // https://github.com/GoogleChrome/puppeteer/issues/1834
+            '--disable-dev-shm-usage',
+          ],
         },
     };
   }
diff --git a/modules/pathkit/karma.conf.js b/modules/pathkit/karma.conf.js
index 47ff2d2..04ef023 100644
--- a/modules/pathkit/karma.conf.js
+++ b/modules/pathkit/karma.conf.js
@@ -60,12 +60,18 @@
     cfg.browsers = ['ChromeHeadlessNoSandbox'],
     cfg.customLaunchers = {
         ChromeHeadlessNoSandbox: {
-            base: 'ChromeHeadless',
-            flags: [
+          base: 'ChromeHeadless',
+          flags: [
             // Without this flag, we see an error:
             // Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted
-                '--no-sandbox'
-            ],
+            '--no-sandbox',
+            // may help tests be less flaky
+            // https://peter.sh/experiments/chromium-command-line-switches/#browser-test
+            '--browser-test',
+            // This can also help avoid crashes/timeouts:
+            // https://github.com/GoogleChrome/puppeteer/issues/1834
+            '--disable-dev-shm-usage',
+          ],
         },
     };
   }
diff --git a/modules/pathkit/pathkit_wasm_bindings.cpp b/modules/pathkit/pathkit_wasm_bindings.cpp
index c47af69..34358d5 100644
--- a/modules/pathkit/pathkit_wasm_bindings.cpp
+++ b/modules/pathkit/pathkit_wasm_bindings.cpp
@@ -275,7 +275,7 @@
     SkPath::Iter iter(path, false);
     SkPoint pts[4];
     SkPath::Verb verb;
-    while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kMove_Verb:
                 ctx.call<void>("moveTo", pts[0].x(), pts[0].y());
@@ -356,9 +356,9 @@
 }
 
 JSString GetFillTypeString(const SkPath& path) {
-    if (path.getFillType() == SkPath::FillType::kWinding_FillType) {
+    if (path.getNewFillType() == SkPathFillType::kWinding) {
         return emscripten::val("nonzero");
-    } else if (path.getFillType() == SkPath::FillType::kEvenOdd_FillType) {
+    } else if (path.getNewFillType() == SkPathFillType::kEvenOdd) {
         return emscripten::val("evenodd");
     } else {
         SkDebugf("warning: can't translate inverted filltype to HTML Canvas\n");
@@ -503,7 +503,7 @@
         .function("_rect", &ApplyAddRect)
 
         // Extra features
-        .function("setFillType", &SkPath::setFillType)
+        .function("setFillType", select_overload<void(SkPathFillType)>(&SkPath::setFillType))
         .function("getFillType", &SkPath::getFillType)
         .function("getFillTypeString", &GetFillTypeString)
         .function("getBounds", &SkPath::getBounds)
@@ -564,11 +564,11 @@
         .value("XOR",                SkPathOp::kXOR_SkPathOp)
         .value("REVERSE_DIFFERENCE", SkPathOp::kReverseDifference_SkPathOp);
 
-    enum_<SkPath::FillType>("FillType")
-        .value("WINDING",            SkPath::FillType::kWinding_FillType)
-        .value("EVENODD",            SkPath::FillType::kEvenOdd_FillType)
-        .value("INVERSE_WINDING",    SkPath::FillType::kInverseWinding_FillType)
-        .value("INVERSE_EVENODD",    SkPath::FillType::kInverseEvenOdd_FillType);
+    enum_<SkPathFillType>("FillType")
+        .value("WINDING",            SkPathFillType::kWinding)
+        .value("EVENODD",            SkPathFillType::kEvenOdd)
+        .value("INVERSE_WINDING",    SkPathFillType::kInverseWinding)
+        .value("INVERSE_EVENODD",    SkPathFillType::kInverseEvenOdd);
 
     constant("MOVE_VERB",  MOVE);
     constant("LINE_VERB",  LINE);
diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn
index 93893ee..bed995f 100644
--- a/modules/skottie/BUILD.gn
+++ b/modules/skottie/BUILD.gn
@@ -24,6 +24,7 @@
     configs += [ "../../:skia_private" ]
     deps = [
       "../..:skia",
+      "../skresources",
       "../sksg",
       "../skshaper",
     ]
diff --git a/modules/skottie/gm/SkottieGM.cpp b/modules/skottie/gm/SkottieGM.cpp
index f95ee1c..e81b406 100644
--- a/modules/skottie/gm/SkottieGM.cpp
+++ b/modules/skottie/gm/SkottieGM.cpp
@@ -11,21 +11,20 @@
 #include "modules/skottie/include/Skottie.h"
 #include "modules/skottie/include/SkottieProperty.h"
 #include "modules/skottie/utils/SkottieUtils.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/core/SkMakeUnique.h"
 #include "tools/Resources.h"
 
 #include <cmath>
 #include <vector>
 
-using namespace skottie;
-
 namespace {
 
 static constexpr char kWebFontResource[] = "fonts/Roboto-Regular.ttf";
 static constexpr char kSkottieResource[] = "skottie/skottie_sample_webfont.json";
 
 // Dummy web font loader which serves a single local font (checked in under resources/).
-class FakeWebFontProvider final : public ResourceProvider {
+class FakeWebFontProvider final : public skresources::ResourceProvider {
 public:
     FakeWebFontProvider() : fFontData(GetResourceAsData(kWebFontResource)) {}
 
@@ -36,7 +35,7 @@
 private:
     sk_sp<SkData> fFontData;
 
-    using INHERITED = ResourceProvider;
+    using INHERITED = skresources::ResourceProvider;
 };
 
 } // namespace
@@ -54,7 +53,7 @@
 
     void onOnceBeforeDraw() override {
         if (auto stream = GetResourceAsStream(kSkottieResource)) {
-            fAnimation = Animation::Builder()
+            fAnimation = skottie::Animation::Builder()
                             .setResourceProvider(sk_make_sp<FakeWebFontProvider>())
                             .make(stream.get());
         }
@@ -84,15 +83,13 @@
 private:
     static constexpr SkScalar kSize = 800;
 
-    sk_sp<Animation> fAnimation;
+    sk_sp<skottie::Animation> fAnimation;
 
     using INHERITED = skiagm::GM;
 };
 
 DEF_GM(return new SkottieWebFontGM;)
 
-using namespace skottie_utils;
-
 class SkottieColorizeGM : public skiagm::GM {
 protected:
     SkString onShortName() override {
@@ -105,8 +102,8 @@
 
     void onOnceBeforeDraw() override {
         if (auto stream = GetResourceAsStream("skottie/skottie_sample_search.json")) {
-            fPropManager = skstd::make_unique<CustomPropertyManager>();
-            fAnimation   = Animation::Builder()
+            fPropManager = skstd::make_unique<skottie_utils::CustomPropertyManager>();
+            fAnimation   = skottie::Animation::Builder()
                               .setPropertyObserver(fPropManager->getPropertyObserver())
                               .make(stream.get());
             fColors      = fPropManager->getColorProps();
@@ -157,10 +154,10 @@
 private:
     static constexpr SkScalar kSize = 800;
 
-    sk_sp<Animation>                            fAnimation;
-    std::unique_ptr<CustomPropertyManager>      fPropManager;
-    std::vector<CustomPropertyManager::PropKey> fColors;
-    size_t                                      fColorIndex = 0;
+    sk_sp<skottie::Animation>                                  fAnimation;
+    std::unique_ptr<skottie_utils::CustomPropertyManager>      fPropManager;
+    std::vector<skottie_utils::CustomPropertyManager::PropKey> fColors;
+    size_t                                                     fColorIndex = 0;
 
     using INHERITED = skiagm::GM;
 };
@@ -180,7 +177,7 @@
 
     void onOnceBeforeDraw() override {
         if (auto stream = GetResourceAsStream("skottie/skottie_sample_multiframe.json")) {
-            fAnimation = Animation::Builder()
+            fAnimation = skottie::Animation::Builder()
                             .setResourceProvider(sk_make_sp<MultiFrameResourceProvider>())
                             .make(stream.get());
         }
@@ -208,17 +205,18 @@
     }
 
 private:
-    class MultiFrameResourceProvider final : public skottie::ResourceProvider {
+    class MultiFrameResourceProvider final : public skresources::ResourceProvider {
     public:
-        sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override {
-            return skottie_utils::MultiFrameImageAsset::Make(
+        sk_sp<skresources::ImageAsset> loadImageAsset(const char[], const char[],
+                                                      const char[]) const override {
+            return skresources::MultiFrameImageAsset::Make(
                         GetResourceAsData("images/flightAnim.gif"));
         }
     };
 
     static constexpr SkScalar kSize = 800;
 
-    sk_sp<Animation> fAnimation;
+    sk_sp<skottie::Animation> fAnimation;
 
     using INHERITED = skiagm::GM;
 };
diff --git a/modules/skottie/include/Skottie.h b/modules/skottie/include/Skottie.h
index 812e744..0741d91 100644
--- a/modules/skottie/include/Skottie.h
+++ b/modules/skottie/include/Skottie.h
@@ -13,12 +13,11 @@
 #include "include/core/SkSize.h"
 #include "include/core/SkString.h"
 #include "include/core/SkTypes.h"
+#include "modules/skresources/include/SkResources.h"
 
 #include <memory>
 
 class SkCanvas;
-class SkData;
-class SkImage;
 struct SkRect;
 class SkStream;
 
@@ -33,70 +32,12 @@
 
 namespace skottie {
 
+using ImageAsset = skresources::ImageAsset;
+using ResourceProvider = skresources::ResourceProvider;
+
 class PropertyObserver;
 
 /**
- * Image asset proxy interface.
- */
-class SK_API ImageAsset : public SkRefCnt {
-public:
-    /**
-     * Returns true if the image asset is animated.
-     */
-    virtual bool isMultiFrame() = 0;
-
-    /**
-     * Returns the SkImage for a given frame.
-     *
-     * If the image asset is static, getImage() is only called once, at animation load time.
-     * Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
-     *
-     * Embedders should cache and serve the same SkImage whenever possible, for efficiency.
-     *
-     * @param t   Frame time code, in seconds, relative to the image layer timeline origin
-     *            (in-point).
-     */
-    virtual sk_sp<SkImage> getFrame(float t) = 0;
-};
-
-/**
- * ResourceProvider allows Skottie embedders to control loading of external
- * Skottie resources -- e.g. images, fonts, nested animations.
- */
-class SK_API ResourceProvider : public SkRefCnt {
-public:
-    /**
-     * Load a generic resource (currently only nested animations) specified by |path| + |name|,
-     * and return as an SkData.
-     */
-    virtual sk_sp<SkData> load(const char resource_path[],
-                               const char resource_name[]) const;
-
-    /**
-     * Load an image asset specified by |path| + |name|, and returns the corresponding
-     * ImageAsset proxy.
-     */
-    virtual sk_sp<ImageAsset> loadImageAsset(const char resource_path[],
-                                             const char resource_name[],
-                                             const char resource_id[]) const;
-
-    /**
-     * Load an external font and return as SkData.
-     *
-     * @param name  font name    ("fName" Lottie property)
-     * @param url   web font URL ("fPath" Lottie property)
-     *
-     * -- Note --
-     *
-     *   This mechanism assumes monolithic fonts (single data blob).  Some web font providers may
-     *   serve multiple font blobs, segmented for various unicode ranges, depending on user agent
-     *   capabilities (woff, woff2).  In that case, the embedder would need to advertise no user
-     *   agent capabilities when fetching the URL, in order to receive full font data.
-     */
-    virtual sk_sp<SkData> loadFont(const char name[], const char url[]) const;
-};
-
-/**
  * A Logger subclass can be used to receive Animation::Builder parsing errors and warnings.
  */
 class SK_API Logger : public SkRefCnt {
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index f8521f1..2710e62 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -14,7 +14,9 @@
 
 skia_skottie_sources = [
   "$_src/Composition.cpp",
+  "$_src/Composition.h",
   "$_src/Layer.cpp",
+  "$_src/Layer.h",
   "$_src/Skottie.cpp",
   "$_src/SkottieAdapter.cpp",
   "$_src/SkottieAdapter.h",
diff --git a/modules/skottie/src/Composition.cpp b/modules/skottie/src/Composition.cpp
index 937f43c..88976b2 100644
--- a/modules/skottie/src/Composition.cpp
+++ b/modules/skottie/src/Composition.cpp
@@ -5,11 +5,12 @@
  * found in the LICENSE file.
  */
 
-#include "modules/skottie/src/SkottiePriv.h"
+#include "modules/skottie/src/Composition.h"
 
 #include "include/core/SkCanvas.h"
 #include "modules/skottie/src/SkottieAdapter.h"
 #include "modules/skottie/src/SkottieJson.h"
+#include "modules/skottie/src/SkottiePriv.h"
 #include "modules/sksg/include/SkSGGroup.h"
 #include "modules/sksg/include/SkSGTransform.h"
 
@@ -118,62 +119,88 @@
     return asset;
 }
 
-sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(
-        const skjson::ObjectValue& jcomp) const {
-    const skjson::ArrayValue* jlayers = jcomp["layers"];
-    if (!jlayers) return nullptr;
-
-    std::vector<sk_sp<sksg::RenderNode>> layers;
-    AttachLayerContext                   layerCtx(*jlayers);
-
+CompositionBuilder::CompositionBuilder(const AnimationBuilder& abuilder,
+                                       const skjson::ObjectValue& jcomp) {
     // Optional motion blur params.
     if (const skjson::ObjectValue* jmb = jcomp["mb"]) {
         static constexpr size_t kMaxSamplesPerFrame = 64;
-        layerCtx.fMotionBlurSamples = std::min(ParseDefault<size_t>((*jmb)["spf"], 1ul),
-                                               kMaxSamplesPerFrame);
-        layerCtx.fMotionBlurAngle = SkTPin(ParseDefault((*jmb)["sa"], 0.0f),    0.0f, 720.0f);
-        layerCtx.fMotionBlurPhase = SkTPin(ParseDefault((*jmb)["sp"], 0.0f), -360.0f, 360.0f);
+        fMotionBlurSamples = std::min(ParseDefault<size_t>((*jmb)["spf"], 1ul),
+                                      kMaxSamplesPerFrame);
+        fMotionBlurAngle = SkTPin(ParseDefault((*jmb)["sa"], 0.0f),    0.0f, 720.0f);
+        fMotionBlurPhase = SkTPin(ParseDefault((*jmb)["sp"], 0.0f), -360.0f, 360.0f);
     }
 
+    int camera_builder_index = -1;
 
-    // First pass: parse layer types and attach camera layers.
-    struct LayerRec {
-        const skjson::ObjectValue& jlayer;
-        size_t                     layer_type;
-    };
-    SkSTArray<64, LayerRec, true> layer_recs(jlayers->size());
-    static constexpr int kCameraLayerType = 13;
-    for (const skjson::ObjectValue* jlayer : *jlayers) {
-        if (!jlayer) {
-            continue;
+    // Prepare layer builders.
+    if (const skjson::ArrayValue* jlayers = jcomp["layers"]) {
+        fLayerBuilders.reserve(SkToInt(jlayers->size()));
+        for (const skjson::ObjectValue* jlayer : *jlayers) {
+            if (!jlayer) continue;
+
+            const auto  lbuilder_index = fLayerBuilders.size();
+            const auto& lbuilder       = fLayerBuilders.emplace_back(*jlayer);
+
+            fLayerIndexMap.set(lbuilder.index(), lbuilder_index);
+
+            // Keep track of the camera builder.
+            if (lbuilder.isCamera()) {
+                // We only support one (first) camera for now.
+                if (camera_builder_index < 0) {
+                    camera_builder_index = SkToInt(lbuilder_index);
+                } else {
+                    abuilder.log(Logger::Level::kWarning, jlayer,
+                                 "Ignoring duplicate camera layer.");
+                }
+            }
         }
-
-        const auto type = ParseDefault<int>((*jlayer)["ty"], -1);
-        if (type < 0) {
-            continue;
-        }
-
-        if (type == kCameraLayerType) {
-            // Cameras must be attached upfront because layer transforms
-            // are rooted in the camera transform.
-            this->attachLayer(*jlayer, kCameraLayerType, &layerCtx);
-            continue;
-        }
-
-        layer_recs.push_back({*jlayer, SkToSizeT(type)});
     }
 
-    if (!layerCtx.fCameraTransform && ParseDefault<int>(jcomp["ddd"], 0)) {
-        // Instantiate a default camera when 3D layers are present.
-        layerCtx.fCameraTransform = CameraAdapter::MakeDefault(fSize)->refTransform();
+    // Attach a camera transform upfront, if needed (required to build
+    // all other 3D transform chains).
+    if (camera_builder_index >= 0) {
+        // Explicit camera.
+        fCameraTransform = fLayerBuilders[camera_builder_index].buildTransform(abuilder, this);
+    } else if (ParseDefault<int>(jcomp["ddd"], 0)) {
+        // Default/implicit camera when 3D layers are present.
+        fCameraTransform = CameraAdapter::MakeDefault(abuilder.fSize)->refTransform();
+    }
+}
+
+CompositionBuilder::~CompositionBuilder() = default;
+
+void CompositionBuilder::pushMatte(sk_sp<sksg::RenderNode> matte) {
+    fCurrentMatte = std::move(matte);
+}
+
+sk_sp<sksg::RenderNode> CompositionBuilder::popMatte() {
+    return std::move(fCurrentMatte);
+}
+
+LayerBuilder* CompositionBuilder::layerBuilder(int layer_index) {
+    if (layer_index < 0) {
+        return nullptr;
     }
 
-    // Second pass: attach all other layers.
-    layers.reserve(layer_recs.size());
-    for (const auto& rec : layer_recs) {
-        SkASSERT(rec.layer_type != kCameraLayerType);
+    if (const auto* idx = fLayerIndexMap.find(layer_index)) {
+        return &fLayerBuilders[SkToInt(*idx)];
+    }
 
-        if (auto layer = this->attachLayer(rec.jlayer, rec.layer_type, &layerCtx)) {
+    return nullptr;
+}
+
+sk_sp<sksg::RenderNode> CompositionBuilder::build(const AnimationBuilder& abuilder) {
+    // First pass - transitively attach layer transform chains.
+    for (auto& lbuilder : fLayerBuilders) {
+        lbuilder.buildTransform(abuilder, this);
+    }
+
+    // Second pass - attach actual layer contents and finalize the layer render tree.
+    std::vector<sk_sp<sksg::RenderNode>> layers;
+    layers.reserve(fLayerBuilders.size());
+
+    for (auto& lbuilder : fLayerBuilders) {
+        if (auto layer = lbuilder.buildRenderTree(abuilder, this)) {
             layers.push_back(std::move(layer));
         }
     }
@@ -182,17 +209,15 @@
         return nullptr;
     }
 
-    sk_sp<sksg::RenderNode> comp;
     if (layers.size() == 1) {
-        comp = std::move(layers[0]);
-    } else {
-        // Layers are painted in bottom->top order.
-        std::reverse(layers.begin(), layers.end());
-        layers.shrink_to_fit();
-        comp = sksg::Group::Make(std::move(layers));
+        return std::move(layers[0]);
     }
 
-    return comp;
+    // Layers are painted in bottom->top order.
+    std::reverse(layers.begin(), layers.end());
+    layers.shrink_to_fit();
+
+    return sksg::Group::Make(std::move(layers));
 }
 
 } // namespace internal
diff --git a/modules/skottie/src/Composition.h b/modules/skottie/src/Composition.h
new file mode 100644
index 0000000..0420dd6
--- /dev/null
+++ b/modules/skottie/src/Composition.h
@@ -0,0 +1,51 @@
+/*
+ * 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 SkottieComposition_DEFINED
+#define SkottieComposition_DEFINED
+
+#include "modules/skottie/src/SkottiePriv.h"
+
+#include "include/private/SkTArray.h"
+#include "include/private/SkTHash.h"
+#include "modules/skottie/src/Layer.h"
+
+namespace skottie {
+namespace internal {
+
+class CompositionBuilder final : SkNoncopyable {
+public:
+    CompositionBuilder(const AnimationBuilder&, const skjson::ObjectValue&);
+    ~CompositionBuilder();
+
+    sk_sp<sksg::RenderNode> build(const AnimationBuilder&);
+
+private:
+    LayerBuilder* layerBuilder(int layer_index);
+
+    const sk_sp<sksg::Transform>& getCameraTransform() const { return fCameraTransform; }
+
+    void pushMatte(sk_sp<sksg::RenderNode>);
+    sk_sp<sksg::RenderNode> popMatte();
+
+    friend class LayerBuilder;
+
+    SkSTArray<64, LayerBuilder> fLayerBuilders;
+    SkTHashMap<int, size_t>     fLayerIndexMap; // Maps layer "ind" to layer builder index.
+
+    sk_sp<sksg::Transform>      fCameraTransform;
+    sk_sp<sksg::RenderNode>     fCurrentMatte;  // Tracks the current/active matte.
+
+    size_t                      fMotionBlurSamples = 1;
+    float                       fMotionBlurAngle   = 0,
+                                fMotionBlurPhase   = 0;
+};
+
+} // namespace internal
+} // namespace skottie
+
+#endif // SkottieComposition_DEFINED
diff --git a/modules/skottie/src/Layer.cpp b/modules/skottie/src/Layer.cpp
index 7a19bbc..e7423cc 100644
--- a/modules/skottie/src/Layer.cpp
+++ b/modules/skottie/src/Layer.cpp
@@ -5,8 +5,9 @@
  * found in the LICENSE file.
  */
 
-#include "modules/skottie/src/SkottiePriv.h"
+#include "modules/skottie/src/Layer.h"
 
+#include "modules/skottie/src/Composition.h"
 #include "modules/skottie/src/SkottieAdapter.h"
 #include "modules/skottie/src/SkottieJson.h"
 #include "modules/skottie/src/effects/Effects.h"
@@ -29,7 +30,6 @@
 namespace  {
 
 static constexpr int kNullLayerType   =  3;
-static constexpr int kCameraLayerType = 13;
 
 struct MaskInfo {
     SkBlendMode       fBlendMode;      // used when masking with layers/blending
@@ -107,8 +107,8 @@
         // "inv" is cumulative with mask info fInvertGeometry
         const auto inverted =
             (mask_info->fInvertGeometry != ParseDefault<bool>((*m)["inv"], false));
-        mask_path->setFillType(inverted ? SkPath::kInverseWinding_FillType
-                                        : SkPath::kWinding_FillType);
+        mask_path->setFillType(inverted ? SkPathFillType::kInverseWinding
+                                        : SkPathFillType::kWinding);
 
         auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
         mask_paint->setAntiAlias(true);
@@ -247,87 +247,92 @@
 
 } // namespace
 
-AnimationBuilder::AttachLayerContext::AttachLayerContext(const skjson::ArrayValue& jlayers)
-    : fLayerList(jlayers) {}
+LayerBuilder::LayerBuilder(const skjson::ObjectValue& jlayer)
+    : fJlayer(jlayer)
+    , fIndex(ParseDefault<int>(jlayer["ind"], -1))
+    , fParentIndex(ParseDefault<int>(jlayer["parent"], -1))
+    , fType(ParseDefault<int>(jlayer["ty"], -1)) {
 
-AnimationBuilder::AttachLayerContext::~AttachLayerContext() = default;
-
-AnimationBuilder::AttachLayerContext::TransformRec
-AnimationBuilder::AttachLayerContext::attachLayerTransform(const skjson::ObjectValue& jlayer,
-                                                           const AnimationBuilder* abuilder,
-                                                           TransformType type,
-                                                           bool has_camera_root) {
-    TransformRec result;
-
-    const auto layer_index = ParseDefault<int>(jlayer["ind"], -1);
-    if (layer_index >= 0) {
-        auto* rec = fLayerTransformMap.find(layer_index);
-        if (!rec) {
-            rec = this->attachLayerTransformImpl(jlayer, abuilder, type,
-                                                 layer_index, has_camera_root);
-        }
-        SkASSERT(rec);
-
-        // Note: the transform animator scope is *moved* to the result, because
-        // we want the animators transferred to the LayerController.
-        //
-        // This is safe because a) the scope is not used internally, and
-        // b) there is exactly one attachLayerTransform call per layer.
-        // The transform node OTOH may be used at a later time for parenting.
-        result.fTransformNode  = rec->fTransformNode;
-        result.fTransformScope = std::move(rec->fTransformScope);
+    if (this->isCamera() || ParseDefault<int>(jlayer["ddd"], 0)) {
+        fFlags |= Flags::kIs3D;
     }
-
-    return result;
 }
 
-sk_sp<sksg::Transform>
-AnimationBuilder::AttachLayerContext::attachParentLayerTransform(const skjson::ObjectValue& jlayer,
-                                                                 const AnimationBuilder* abuilder,
-                                                                 int layer_index,
-                                                                 bool has_camera_root) {
-    const auto parent_index = ParseDefault<int>(jlayer["parent"], -1);
-    if (parent_index < 0 || parent_index == layer_index) {
-        // 3D layer transform chains are implicitly rooted in the camera transform
-        // (except for camera parent layers).
-        return has_camera_root ? fCameraTransform : nullptr;
+LayerBuilder::~LayerBuilder() = default;
+
+bool LayerBuilder::isCamera() const {
+    static constexpr int kCameraLayerType = 13;
+
+    return fType == kCameraLayerType;
+}
+
+sk_sp<sksg::Transform> LayerBuilder::buildTransform(const AnimationBuilder& abuilder,
+                                                    CompositionBuilder* cbuilder) {
+    // Depending on the leaf node type, we treat the whole transform chain as either 2D or 3D.
+    const auto transform_chain_type = this->is3D() ? TransformType::k3D
+                                                   : TransformType::k2D;
+    fLayerTransform = this->getTransform(abuilder, cbuilder, transform_chain_type);
+
+    return fLayerTransform;
+}
+
+sk_sp<sksg::Transform> LayerBuilder::getTransform(const AnimationBuilder& abuilder,
+                                                  CompositionBuilder* cbuilder,
+                                                  TransformType ttype) {
+    const auto cache_valid_mask = (1ul << ttype);
+    if (!(fFlags & cache_valid_mask)) {
+        // Set valid flag upfront to break cycles.
+        fFlags |= cache_valid_mask;
+
+        const AnimationBuilder::AutoPropertyTracker apt(&abuilder, fJlayer);
+        AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
+        fTransformCache[ttype] = this->doAttachTransform(abuilder, cbuilder, ttype);
+        fLayerScope = ascope.release();
+        fTransformAnimatorCount = fLayerScope.size();
     }
 
-    if (const auto* rec = fLayerTransformMap.find(parent_index))
-        return rec->fTransformNode;
+    return fTransformCache[ttype];
+}
 
-    for (const skjson::ObjectValue* l : fLayerList) {
-        if (!l) continue;
+sk_sp<sksg::Transform> LayerBuilder::getParentTransform(const AnimationBuilder& abuilder,
+                                                        CompositionBuilder* cbuilder,
+                                                        TransformType ttype) {
+    if (auto* parent_builder = cbuilder->layerBuilder(fParentIndex)) {
+        // Explicit parent layer.
+        return parent_builder->getTransform(abuilder, cbuilder, ttype);
+    }
 
-        if (ParseDefault<int>((*l)["ind"], -1) == parent_index) {
-            const auto parent_type = ParseDefault<int>((*l)["ty"], -1) == kCameraLayerType
-                    ? TransformType::kCamera
-                    : TransformType::kLayer;
-            return this->attachLayerTransformImpl(*l,
-                                                  abuilder,
-                                                  parent_type,
-                                                  parent_index,
-                                                  has_camera_root)->fTransformNode;
-        }
+    if (ttype == TransformType::k3D) {
+        // During camera transform attachment, cbuilder->getCameraTransform() is null.
+        // This prevents camera->camera transform chain cycles.
+        SkASSERT(!this->isCamera() || !cbuilder->getCameraTransform());
+
+        // 3D transform chains are implicitly rooted onto the camera.
+        return cbuilder->getCameraTransform();
     }
 
     return nullptr;
 }
 
-sk_sp<sksg::Transform>
-AnimationBuilder::AttachLayerContext::attachTransformNode(const skjson::ObjectValue& jlayer,
-                                                          const AnimationBuilder* abuilder,
-                                                          sk_sp<sksg::Transform> parent_transform,
-                                                          TransformType type) const {
-    const skjson::ObjectValue* jtransform = jlayer["ks"];
+sk_sp<sksg::Transform> LayerBuilder::doAttachTransform(const AnimationBuilder& abuilder,
+                                                       CompositionBuilder* cbuilder,
+                                                       TransformType ttype) {
+    const skjson::ObjectValue* jtransform = fJlayer["ks"];
     if (!jtransform) {
         return nullptr;
     }
 
-    if (type == TransformType::kCamera) {
-        auto camera_adapter = sk_make_sp<CameraAdapter>(abuilder->fSize);
+    auto parent_transform = this->getParentTransform(abuilder, cbuilder, ttype);
 
-        abuilder->bindProperty<ScalarValue>(jlayer["pe"],
+    if (this->isCamera()) {
+        // The presence of an anchor point property ('a') differentiates
+        // one-node vs. two-node cameras.
+        const auto camera_type = (*jtransform)["a"].is<skjson::NullValue>()
+                ? CameraAdapter::Type::kOneNode
+                : CameraAdapter::Type::kTwoNode;
+        auto camera_adapter = sk_make_sp<CameraAdapter>(abuilder.fSize, camera_type);
+
+        abuilder.bindProperty<ScalarValue>(fJlayer["pe"],
             [camera_adapter] (const ScalarValue& pe) {
                 // 'pe' (perspective?) corresponds to AE's "zoom" camera property.
                 camera_adapter->setZoom(pe);
@@ -340,64 +345,42 @@
         //
         parent_transform = sksg::Transform::MakeInverse(std::move(parent_transform));
 
-        return abuilder->attachMatrix3D(*jtransform,
-                                        std::move(parent_transform),
-                                        std::move(camera_adapter),
-                                        true); // pre-compose parent
+        return abuilder.attachMatrix3D(*jtransform,
+                                       std::move(parent_transform),
+                                       std::move(camera_adapter),
+                                       true); // pre-compose parent
     }
 
-    return (ParseDefault<int>(jlayer["ddd"], 0) == 0)
-            ? abuilder->attachMatrix2D(*jtransform, std::move(parent_transform))
-            : abuilder->attachMatrix3D(*jtransform, std::move(parent_transform));
+    return this->is3D()
+            ? abuilder.attachMatrix3D(*jtransform, std::move(parent_transform))
+            : abuilder.attachMatrix2D(*jtransform, std::move(parent_transform));
 }
 
-AnimationBuilder::AttachLayerContext::TransformRec*
-AnimationBuilder::AttachLayerContext::attachLayerTransformImpl(const skjson::ObjectValue& jlayer,
-                                                               const AnimationBuilder* abuilder,
-                                                               TransformType type,
-                                                               int layer_index,
-                                                               bool has_camera_root) {
-    SkASSERT(!fLayerTransformMap.find(layer_index));
-
-    // Add a stub entry to break recursion cycles.
-    fLayerTransformMap.set(layer_index, { nullptr, {} });
-
-    has_camera_root &= type != TransformType::kCamera;
-    auto parent_matrix = this->attachParentLayerTransform(jlayer, abuilder, layer_index,
-                                                          has_camera_root);
-    AutoScope ascope(abuilder);
-    auto transform = this->attachTransformNode(jlayer,
-                                               abuilder,
-                                               std::move(parent_matrix),
-                                               type);
-
-    return fLayerTransformMap.set(layer_index, { std::move(transform), ascope.release() });
+bool LayerBuilder::hasMotionBlur(const CompositionBuilder* cbuilder) const {
+    return cbuilder->fMotionBlurSamples > 1
+        && cbuilder->fMotionBlurAngle   > 0
+        && ParseDefault(fJlayer["mb"], false);
 }
 
-bool AnimationBuilder::AttachLayerContext::hasMotionBlur(const skjson::ObjectValue& jlayer) const {
-    return fMotionBlurSamples > 1
-        && fMotionBlurAngle   > 0
-        && ParseDefault(jlayer["mb"], false);
-}
-
-sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue& jlayer,
-                                                      size_t type,
-                                                      AttachLayerContext* layerCtx) const {
-    LayerInfo layer_info = {
-        fSize,
-        ParseDefault<float>(jlayer["ip"], 0.0f),
-        ParseDefault<float>(jlayer["op"], 0.0f),
+sk_sp<sksg::RenderNode> LayerBuilder::buildRenderTree(const AnimationBuilder& abuilder,
+                                                      CompositionBuilder* cbuilder) {
+    AnimationBuilder::LayerInfo layer_info = {
+        abuilder.fSize,
+        ParseDefault<float>(fJlayer["ip"], 0.0f),
+        ParseDefault<float>(fJlayer["op"], 0.0f),
     };
     if (layer_info.fInPoint >= layer_info.fOutPoint) {
-        this->log(Logger::Level::kError, nullptr,
-                  "Invalid layer in/out points: %f/%f.", layer_info.fInPoint, layer_info.fOutPoint);
+        abuilder.log(Logger::Level::kError, nullptr,
+                     "Invalid layer in/out points: %f/%f.",
+                     layer_info.fInPoint, layer_info.fOutPoint);
         return nullptr;
     }
 
-    const AutoPropertyTracker apt(this, jlayer);
+    const AnimationBuilder::AutoPropertyTracker apt(&abuilder, fJlayer);
 
-    using LayerBuilder = sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
-                                                                       LayerInfo*) const;
+    using LayerBuilder =
+        sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
+                                                      AnimationBuilder::LayerInfo*) const;
 
     // AE is annoyingly inconsistent in how effects interact with layer transforms: depending on
     // the layer type, effects are applied before or after the content is transformed.
@@ -421,131 +404,108 @@
         { &AnimationBuilder::attachTextLayer   ,                 0 },  // 'ty': 5 -> text
     };
 
-    if (type >= SK_ARRAY_COUNT(gLayerBuildInfo) && type != kCameraLayerType) {
+    if (SkToSizeT(fType) >= SK_ARRAY_COUNT(gLayerBuildInfo) && !this->isCamera()) {
         return nullptr;
     }
 
-    // Optional layer transform.
-    const auto transform_type = (type == kCameraLayerType)
-            ? AttachLayerContext::TransformType::kCamera
-            : AttachLayerContext::TransformType::kLayer;
+    // Switch to the layer animator scope (which at this point holds transform-only animators).
+    AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
 
-    // Only layers tagged as 3D observe the composition camera.
-    // TODO: also "2D layers with an effect with a Comp Camera attribute".
-    const auto has_camera_root = transform_type != AttachLayerContext::TransformType::kCamera
-                              && ParseDefault<int>(jlayer["ddd"], 0);
-
-    auto layer_transform_rec = layerCtx->attachLayerTransform(jlayer, this, transform_type,
-                                                              has_camera_root);
-    if (type == kCameraLayerType) {
-        // Camera layers are special: they don't build normal SG fragments, but drive a root-level
-        // transform.
-        if (layerCtx->fCameraTransform) {
-            this->log(Logger::Level::kWarning, &jlayer, "Ignoring duplicate camera layer.");
-            return nullptr;
-        }
-
-        layerCtx->fCameraTransform = layer_transform_rec.fTransformNode;
-    }
-
-    AutoScope ascope(this, std::move(layer_transform_rec.fTransformScope));
-    const auto transform_animator_count = fCurrentAnimatorScope->size();
-
-    const auto is_hidden = ParseDefault<bool>(jlayer["hd"], false) || type == kCameraLayerType;
-    const auto& build_info = gLayerBuildInfo[is_hidden ? kNullLayerType : type];
+    const auto is_hidden = ParseDefault<bool>(fJlayer["hd"], false) || this->isCamera();
+    const auto& build_info = gLayerBuildInfo[is_hidden ? kNullLayerType : SkToSizeT(fType)];
 
     // Build the layer content fragment.
-    auto layer = (this->*(build_info.fBuilder))(jlayer, &layer_info);
+    auto layer = (abuilder.*(build_info.fBuilder))(fJlayer, &layer_info);
 
     // Clip layers with explicit dimensions.
     float w = 0, h = 0;
-    if (Parse<float>(jlayer["w"], &w) && Parse<float>(jlayer["h"], &h)) {
+    if (Parse<float>(fJlayer["w"], &w) && Parse<float>(fJlayer["h"], &h)) {
         layer = sksg::ClipEffect::Make(std::move(layer),
                                        sksg::Rect::Make(SkRect::MakeWH(w, h)),
                                        true);
     }
 
     // Optional layer mask.
-    layer = AttachMask(jlayer["masksProperties"], this, std::move(layer));
+    layer = AttachMask(fJlayer["masksProperties"], &abuilder, std::move(layer));
 
     // Does the transform apply to effects also?
     // (AE quirk: it doesn't - except for solid layers)
     const auto transform_effects = (build_info.fFlags & kTransformEffects);
 
     // Attach the transform before effects, when needed.
-    if (layer_transform_rec.fTransformNode && !transform_effects) {
-        layer = sksg::TransformEffect::Make(std::move(layer), layer_transform_rec.fTransformNode);
+    if (fLayerTransform && !transform_effects) {
+        layer = sksg::TransformEffect::Make(std::move(layer), fLayerTransform);
     }
 
     // Optional layer effects.
-    if (const skjson::ArrayValue* jeffects = jlayer["ef"]) {
-        layer = EffectBuilder(this, layer_info.fSize).attachEffects(*jeffects, std::move(layer));
+    if (const skjson::ArrayValue* jeffects = fJlayer["ef"]) {
+        layer = EffectBuilder(&abuilder, layer_info.fSize).attachEffects(*jeffects,
+                                                                         std::move(layer));
     }
 
     // Attach the transform after effects, when needed.
-    if (layer_transform_rec.fTransformNode && transform_effects) {
-        layer = sksg::TransformEffect::Make(std::move(layer),
-                                            std::move(layer_transform_rec.fTransformNode));
+    if (fLayerTransform && transform_effects) {
+        layer = sksg::TransformEffect::Make(std::move(layer), std::move(fLayerTransform));
     }
 
     // Optional layer opacity.
     // TODO: de-dupe this "ks" lookup with matrix above.
-    if (const skjson::ObjectValue* jtransform = jlayer["ks"]) {
-        layer = this->attachOpacity(*jtransform, std::move(layer));
+    if (const skjson::ObjectValue* jtransform = fJlayer["ks"]) {
+        layer = abuilder.attachOpacity(*jtransform, std::move(layer));
     }
 
-    // Optional blend mode.
-    layer = this->attachBlendMode(jlayer, std::move(layer));
-
-    const auto has_animators = !fCurrentAnimatorScope->empty();
+    const auto has_animators = !abuilder.fCurrentAnimatorScope->empty();
 
     sk_sp<sksg::Animator> controller = sk_make_sp<LayerController>(ascope.release(),
                                                                    layer,
-                                                                   transform_animator_count,
+                                                                   fTransformAnimatorCount,
                                                                    layer_info.fInPoint,
                                                                    layer_info.fOutPoint);
 
     // Optional motion blur.
-    if (layer && has_animators && layerCtx->hasMotionBlur(jlayer)) {
-        SkASSERT(layerCtx->fMotionBlurAngle >= 0);
-
+    if (layer && has_animators && this->hasMotionBlur(cbuilder)) {
         // Wrap both the layer node and the controller.
         auto motion_blur = MotionBlurEffect::Make(std::move(controller), std::move(layer),
-                                                  layerCtx->fMotionBlurSamples,
-                                                  layerCtx->fMotionBlurAngle,
-                                                  layerCtx->fMotionBlurPhase);
+                                                  cbuilder->fMotionBlurSamples,
+                                                  cbuilder->fMotionBlurAngle,
+                                                  cbuilder->fMotionBlurPhase);
         controller = sk_make_sp<MotionBlurController>(motion_blur);
         layer = std::move(motion_blur);
     }
 
-    fCurrentAnimatorScope->push_back(std::move(controller));
+    abuilder.fCurrentAnimatorScope->push_back(std::move(controller));
 
     if (!layer) {
         return nullptr;
     }
 
-    if (ParseDefault<bool>(jlayer["td"], false)) {
-        // This layer is a matte.  We apply it as a mask to the next layer.
-        layerCtx->fCurrentMatte = std::move(layer);
-        return nullptr;
-    }
-
-    if (layerCtx->fCurrentMatte) {
-        // There is a pending matte. Apply and reset.
+    if (auto matte = cbuilder->popMatte()) {
+        // There is a pending matte (|layer| is a matte target).
         static constexpr sksg::MaskEffect::Mode gMaskModes[] = {
             sksg::MaskEffect::Mode::kAlphaNormal, // tt: 1
             sksg::MaskEffect::Mode::kAlphaInvert, // tt: 2
             sksg::MaskEffect::Mode::kLumaNormal,  // tt: 3
             sksg::MaskEffect::Mode::kLumaInvert,  // tt: 4
         };
-        const auto matteType = ParseDefault<size_t>(jlayer["tt"], 1) - 1;
+        const auto matteType = ParseDefault<size_t>(fJlayer["tt"], 1) - 1;
 
         if (matteType < SK_ARRAY_COUNT(gMaskModes)) {
-            return sksg::MaskEffect::Make(std::move(layer),
-                                          std::move(layerCtx->fCurrentMatte),
-                                          gMaskModes[matteType]);
+            layer = sksg::MaskEffect::Make(std::move(layer),
+                                           std::move(matte),
+                                           gMaskModes[matteType]);
         }
-        layerCtx->fCurrentMatte.reset();
+    }
+
+    // Optional blend mode.  The attachment point is important for matte interactions:
+    //   - for mattes (mask layers), the blend mode is applied to the layer content
+    //   - for matte targets (masked layers), the blend mode is applied post-masking
+    //     (wrapping the MaskEffect above)
+    layer = abuilder.attachBlendMode(fJlayer, std::move(layer));
+
+    if (ParseDefault<bool>(fJlayer["td"], false)) {
+        // |layer| is a matte.  We apply it as a mask to the next layer.
+        cbuilder->pushMatte(std::move(layer));
+        return nullptr;
     }
 
     return layer;
diff --git a/modules/skottie/src/Layer.h b/modules/skottie/src/Layer.h
new file mode 100644
index 0000000..d85a52b
--- /dev/null
+++ b/modules/skottie/src/Layer.h
@@ -0,0 +1,77 @@
+/*
+ * 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 SkottieLayer_DEFINED
+#define SkottieLayer_DEFINED
+
+#include "modules/skottie/src/SkottiePriv.h"
+
+namespace skottie {
+namespace internal {
+
+class CompositionBuilder;
+
+class LayerBuilder final {
+public:
+    explicit LayerBuilder(const skjson::ObjectValue& jlayer);
+    ~LayerBuilder();
+
+    int index() const { return fIndex; }
+
+    bool isCamera() const;
+
+    // Attaches the local and ancestor transform chain for the layer "native" type.
+    sk_sp<sksg::Transform> buildTransform(const AnimationBuilder&, CompositionBuilder*);
+
+    // Attaches the actual layer content and finalizes its render tree.  Called once per layer.
+    sk_sp<sksg::RenderNode> buildRenderTree(const AnimationBuilder&, CompositionBuilder*);
+
+private:
+    enum TransformType : uint8_t {
+        k2D = 0,
+        k3D = 1,
+    };
+
+    enum Flags {
+        // k2DTransformValid = 0x01,  // reserved for cache tracking
+        // k3DTransformValie = 0x02,  // reserved for cache tracking
+        kIs3D                = 0x04,  // 3D layer ("ddd": 1) or camera layer
+    };
+
+    bool is3D() const { return fFlags & Flags::kIs3D; }
+
+    bool hasMotionBlur(const CompositionBuilder*) const;
+
+    // Attaches (if needed) and caches the transform chain for a given layer,
+    // as either a 2D or 3D chain type.
+    // Called transitively (and possibly repeatedly) to resolve layer parenting.
+    sk_sp<sksg::Transform> getTransform(const AnimationBuilder&, CompositionBuilder*,
+                                        TransformType);
+
+    sk_sp<sksg::Transform> getParentTransform(const AnimationBuilder&, CompositionBuilder*,
+                                              TransformType);
+
+    sk_sp<sksg::Transform> doAttachTransform(const AnimationBuilder&, CompositionBuilder*,
+                                             TransformType);
+
+    const skjson::ObjectValue& fJlayer;
+    const int                  fIndex;
+    const int                  fParentIndex;
+    const int                  fType;
+
+    sk_sp<sksg::Transform>     fLayerTransform;             // this layer's transform node.
+    sk_sp<sksg::Transform>     fTransformCache[2];          // cached 2D/3D chain for the local node
+
+    AnimatorScope              fLayerScope;                 // layer-scoped animators
+    size_t                     fTransformAnimatorCount = 0; // transform-related animator count
+    uint32_t                   fFlags                  = 0;
+};
+
+} // namespace internal
+} // namespace skottie
+
+#endif // SkottieLayer_DEFINED
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index df51dc9..9479806 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -17,6 +17,7 @@
 #include "include/private/SkTArray.h"
 #include "include/private/SkTo.h"
 #include "modules/skottie/include/SkottieProperty.h"
+#include "modules/skottie/src/Composition.h"
 #include "modules/skottie/src/SkottieAdapter.h"
 #include "modules/skottie/src/SkottieJson.h"
 #include "modules/skottie/src/SkottiePriv.h"
@@ -289,7 +290,7 @@
     this->parseFonts(jroot["fonts"], jroot["chars"]);
 
     AutoScope ascope(this);
-    auto root = this->attachComposition(jroot);
+    auto root = CompositionBuilder(*this, jroot).build(*this);
 
     auto animators = ascope.release();
     fStats->fAnimatorCount = animators.size();
@@ -404,19 +405,6 @@
 
 } // namespace internal
 
-sk_sp<SkData> ResourceProvider::load(const char[], const char[]) const {
-    return nullptr;
-}
-
-sk_sp<ImageAsset> ResourceProvider::loadImageAsset(const char path[], const char name[],
-                                                   const char id[]) const {
-    return nullptr;
-}
-
-sk_sp<SkData> ResourceProvider::loadFont(const char[], const char[]) const {
-    return nullptr;
-}
-
 void Logger::log(Level, const char[], const char*) {}
 
 Animation::Builder::Builder()  = default;
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp
index b3f34fd..1cad3d9 100644
--- a/modules/skottie/src/SkottieAdapter.cpp
+++ b/modules/skottie/src/SkottieAdapter.cpp
@@ -120,42 +120,57 @@
     fMatrixNode->setMatrix(this->totalMatrix());
 }
 
-CameraAdapter:: CameraAdapter(const SkSize& viewport_size)
-    : fViewportSize(viewport_size) {}
+CameraAdapter:: CameraAdapter(const SkSize& viewport_size, Type type)
+    : fViewportSize(viewport_size)
+    , fType(type)
+{}
 
 CameraAdapter::~CameraAdapter() = default;
 
 sk_sp<CameraAdapter> CameraAdapter::MakeDefault(const SkSize &viewport_size) {
-    auto adapter = sk_make_sp<CameraAdapter>(viewport_size);
+    auto adapter = sk_make_sp<CameraAdapter>(viewport_size, Type::kOneNode);
 
     static constexpr float kDefaultAEZoom = 879.13f;
     const auto center = SkVector::Make(viewport_size.width()  * 0.5f,
                                        viewport_size.height() * 0.5f);
     adapter->setZoom(kDefaultAEZoom);
-    adapter->setAnchorPoint(TransformAdapter3D::Vec3({center.fX, center.fY, 0}));
     adapter->setPosition   (TransformAdapter3D::Vec3({center.fX, center.fY, -kDefaultAEZoom}));
 
     return adapter;
 }
 
+SkPoint3 CameraAdapter::poi() const {
+    // AE supports two camera types:
+    //
+    //   - one-node camera: does not auto-orient, and starts off perpendicular to the z = 0 plane,
+    //     facing "forward" (decreasing z).
+    //
+    //   - two-node camera: has a point of interest (encoded as the anchor point), and auto-orients
+    //                      to point in its direction.
+    return fType == Type::kOneNode
+            ? SkPoint3{ this->getPosition().fX,
+                        this->getPosition().fY,
+                       -this->getPosition().fZ - 1 }
+            : SkPoint3{ this->getAnchorPoint().fX,
+                        this->getAnchorPoint().fY,
+                       -this->getAnchorPoint().fZ};
+}
+
 SkMatrix44 CameraAdapter::totalMatrix() const {
     // Camera parameters:
     //
     //   * location          -> position attribute
-    //   * point of interest -> anchor point attribute
+    //   * point of interest -> anchor point attribute (two-node camera only)
     //   * orientation       -> rotation attribute
     //
-    SkPoint3 pos = { this->getPosition().fX,
-                     this->getPosition().fY,
-                    -this->getPosition().fZ },
-             poi = { this->getAnchorPoint().fX,
-                     this->getAnchorPoint().fY,
-                    -this->getAnchorPoint().fZ },
-              up = { 0, 1, 0 };
+    const auto pos = SkPoint3{ this->getPosition().fX,
+                               this->getPosition().fY,
+                              -this->getPosition().fZ },
+                up = SkPoint3{ 0, 1, 0 };
 
     // Initial camera vector.
     SkMatrix44 cam_t;
-    Sk3LookAt(&cam_t, pos, poi, up);
+    Sk3LookAt(&cam_t, pos, this->poi(), up);
 
     // Rotation origin is camera position.
     {
@@ -265,34 +280,115 @@
     fPathNode->setPath(poly);
 }
 
-GradientAdapter::GradientAdapter(sk_sp<sksg::Gradient> grad, size_t stopCount)
+GradientAdapter::GradientAdapter(sk_sp<sksg::Gradient> grad, size_t colorStopCount)
     : fGradient(std::move(grad))
-    , fStopCount(stopCount) {}
+    , fColorStopCount(colorStopCount) {}
+
+template <typename T>
+static inline const T* next_rec(const T* rec, const T* end_rec) {
+    if (!rec) return nullptr;
+
+    SkASSERT(rec < end_rec);
+    rec++;
+
+    return rec < end_rec ? rec : nullptr;
+};
 
 void GradientAdapter::apply() {
     this->onApply();
 
-    // |fColorStops| holds |fStopCount| x [ pos, r, g, g ] + ? x [ pos, alpha ]
+    // Gradient color stops are specified as a consolidated float vector holding:
+    //
+    //   a) an (optional) array of color/RGB stop records (t, r, g, b)
+    //
+    // followed by
+    //
+    //   b) an (optional) array of opacity/alpha stop records (t, a)
+    //
+    struct   ColorRec { float t, r, g, b; };
+    struct OpacityRec { float t, a;       };
 
-    if (fColorStops.size() < fStopCount * 4 || ((fColorStops.size() - fStopCount * 4) % 2)) {
+    // The number of color records is explicit (fColorStopCount),
+    // while the number of opacity stops is implicit (based on the size of fStops).
+    //
+    // |fStops| holds ColorRec x |fColorStopCount| + OpacityRec x N
+    const auto c_count = fColorStopCount,
+               c_size  = c_count * 4,
+               o_count = (fStops.size() - c_size) / 2;
+    if (fStops.size() < c_size || fStops.size() != (c_count * 4 + o_count * 2)) {
         // apply() may get called before the stops are set, so only log when we have some stops.
-        if (!fColorStops.empty()) {
-            SkDebugf("!! Invalid gradient stop array size: %zu\n", fColorStops.size());
+        if (!fStops.empty()) {
+            SkDebugf("!! Invalid gradient stop array size: %zu\n", fStops.size());
         }
         return;
     }
 
+    const auto* c_rec = c_count > 0 ? reinterpret_cast<const ColorRec*>(fStops.data())
+                                    : nullptr;
+    const auto* o_rec = o_count > 0 ? reinterpret_cast<const OpacityRec*>(fStops.data() + c_size)
+                                    : nullptr;
+    const auto* c_end = c_rec + c_count;
+    const auto* o_end = o_rec + o_count;
+
+    sksg::Gradient::ColorStop current_stop = {
+        0.0f, {
+            c_rec ? c_rec->r : 0,
+            c_rec ? c_rec->g : 0,
+            c_rec ? c_rec->b : 0,
+            o_rec ? o_rec->a : 1,
+    }};
+
     std::vector<sksg::Gradient::ColorStop> stops;
+    stops.reserve(c_count);
 
-    // TODO: merge/lerp opacity stops
-    const auto csEnd = fColorStops.cbegin() + fStopCount * 4;
-    for (auto cs = fColorStops.cbegin(); cs != csEnd; cs += 4) {
-        const auto pos = cs[0];
-        const VectorValue rgb({ cs[1], cs[2], cs[3] });
+    // Merge-sort the color and opacity stops, LERP-ing intermediate channel values as needed.
+    while (c_rec || o_rec) {
+        // After exhausting one of color recs / opacity recs, continue propagating the last
+        // computed values (as if they were specified at the current position).
+        const auto& cs = c_rec
+                ? *c_rec
+                : ColorRec{ o_rec->t,
+                            current_stop.fColor.fR,
+                            current_stop.fColor.fG,
+                            current_stop.fColor.fB };
+        const auto& os = o_rec
+                ? *o_rec
+                : OpacityRec{ c_rec->t, current_stop.fColor.fA };
 
-        stops.push_back({ pos, ValueTraits<VectorValue>::As<SkColor>(rgb) });
+        // Compute component lerp coefficients based on the relative position of the stops
+        // being considered. The idea is to select the smaller-pos stop, use its own properties
+        // as specified (lerp with t == 1), and lerp (with t < 1) the properties from the
+        // larger-pos stop against the previously computed gradient stop values.
+        const auto     c_pos = std::max(cs.t, current_stop.fPosition),
+                       o_pos = std::max(os.t, current_stop.fPosition),
+                   c_pos_rel = c_pos - current_stop.fPosition,
+                   o_pos_rel = o_pos - current_stop.fPosition,
+                         t_c = SkTPin(sk_ieee_float_divide(o_pos_rel, c_pos_rel), 0.0f, 1.0f),
+                         t_o = SkTPin(sk_ieee_float_divide(c_pos_rel, o_pos_rel), 0.0f, 1.0f);
+
+        auto lerp = [](float a, float b, float t) { return a + t * (b - a); };
+
+        current_stop = {
+                std::min(c_pos, o_pos),
+                {
+                    lerp(current_stop.fColor.fR, cs.r, t_c ),
+                    lerp(current_stop.fColor.fG, cs.g, t_c ),
+                    lerp(current_stop.fColor.fB, cs.b, t_c ),
+                    lerp(current_stop.fColor.fA, os.a, t_o)
+                }
+        };
+        stops.push_back(current_stop);
+
+        // Consume one of, or both (for coincident positions) color/opacity stops.
+        if (c_pos <= o_pos) {
+            c_rec = next_rec<ColorRec>(c_rec, c_end);
+        }
+        if (o_pos <= c_pos) {
+            o_rec = next_rec<OpacityRec>(o_rec, o_end);
+        }
     }
 
+    stops.shrink_to_fit();
     fGradient->setColorStops(std::move(stops));
 }
 
diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h
index 75ec9a3..8693ab6 100644
--- a/modules/skottie/src/SkottieAdapter.h
+++ b/modules/skottie/src/SkottieAdapter.h
@@ -172,9 +172,14 @@
 
 class CameraAdapter final : public TransformAdapter3D {
 public:
+    enum class Type {
+        kOneNode, // implicitly facing forward (decreasing z), does not auto-orient
+        kTwoNode, // explicitly facing a POI (the anchor point), auto-orients
+    };
+
     static sk_sp<CameraAdapter> MakeDefault(const SkSize& viewport_size);
 
-    explicit CameraAdapter(const SkSize& viewport_size);
+    CameraAdapter(const SkSize& viewport_size, Type);
     ~CameraAdapter() override;
 
     ADAPTER_PROPERTY(Zoom, SkScalar, 0)
@@ -182,7 +187,10 @@
 private:
     SkMatrix44 totalMatrix() const override;
 
+    SkPoint3 poi() const;
+
     const SkSize fViewportSize;
+    const Type   fType;
 
     using INHERITED = TransformAdapter3D;
 };
@@ -221,16 +229,16 @@
 public:
     ADAPTER_PROPERTY(StartPoint, SkPoint        , SkPoint::Make(0, 0)   )
     ADAPTER_PROPERTY(EndPoint  , SkPoint        , SkPoint::Make(0, 0)   )
-    ADAPTER_PROPERTY(ColorStops, VectorValue    , VectorValue()         )
+    ADAPTER_PROPERTY(Stops     , VectorValue    , VectorValue()         )
 
 protected:
-    GradientAdapter(sk_sp<sksg::Gradient>, size_t stopCount);
+    GradientAdapter(sk_sp<sksg::Gradient>, size_t colorStopCount);
 
     const SkPoint& startPoint() const { return fStartPoint; }
     const SkPoint& endPoint()   const { return fEndPoint;   }
 
     sk_sp<sksg::Gradient> fGradient;
-    size_t                fStopCount;
+    size_t                fColorStopCount;
 
     virtual void onApply() = 0;
 
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index 7a68cef..e3abab8 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -166,6 +166,9 @@
     bool dispatchTransformProperty(const sk_sp<TransformAdapter2D>&) const;
 
 private:
+    friend class CompositionBuilder;
+    friend class LayerBuilder;
+
     struct AttachLayerContext;
     struct AttachShapeContext;
     struct ImageAssetInfo;
@@ -177,10 +180,6 @@
 
     void dispatchMarkers(const skjson::ArrayValue*) const;
 
-    sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&) const;
-    sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue&, size_t type,
-                                        AttachLayerContext*) const;
-
     sk_sp<sksg::RenderNode> attachBlendMode(const skjson::ObjectValue&,
                                             sk_sp<sksg::RenderNode>) const;
 
@@ -253,50 +252,6 @@
     using INHERITED = SkNoncopyable;
 };
 
-struct AnimationBuilder::AttachLayerContext {
-    explicit AttachLayerContext(const skjson::ArrayValue&);
-    ~AttachLayerContext();
-
-    struct TransformRec {
-        sk_sp<sksg::Transform> fTransformNode;
-        AnimatorScope          fTransformScope;
-    };
-
-    const skjson::ArrayValue&     fLayerList;
-    SkTHashMap<int, TransformRec> fLayerTransformMap;
-    sk_sp<sksg::RenderNode>       fCurrentMatte;
-    sk_sp<sksg::Transform>        fCameraTransform;
-
-    size_t                        fMotionBlurSamples = 1;
-    float                         fMotionBlurAngle   = 0,
-                                  fMotionBlurPhase   = 0;
-
-    enum class TransformType { kLayer, kCamera };
-
-    TransformRec attachLayerTransform(const skjson::ObjectValue& jlayer,
-                                      const AnimationBuilder* abuilder,
-                                      TransformType type,
-                                      bool has_camera_root);
-
-    bool hasMotionBlur(const skjson::ObjectValue& jlayer) const;
-
-private:
-    sk_sp<sksg::Transform> attachParentLayerTransform(const skjson::ObjectValue& jlayer,
-                                                      const AnimationBuilder* abuilder,
-                                                      int layer_index,
-                                                      bool has_camera_root);
-
-    sk_sp<sksg::Transform> attachTransformNode(const skjson::ObjectValue& jlayer,
-                                               const AnimationBuilder* abuilder,
-                                               sk_sp<sksg::Transform> parent_transform,
-                                               TransformType type) const;
-
-    TransformRec* attachLayerTransformImpl(const skjson::ObjectValue& jlayer,
-                                           const AnimationBuilder* abuilder,
-                                           TransformType type, int layer_index,
-                                           bool has_camera_root);
-};
-
 } // namespace internal
 } // namespace skottie
 
diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp
index 29124a2..991348d 100644
--- a/modules/skottie/src/SkottieTest.cpp
+++ b/modules/skottie/src/SkottieTest.cpp
@@ -253,7 +253,7 @@
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(opacities[1].handle->get(), 50));
 
     const auto& transforms = observer->transforms();
-    REPORTER_ASSERT(reporter, transforms.size() == 2);
+    REPORTER_ASSERT(reporter, transforms.size() == 3);
     REPORTER_ASSERT(reporter, transforms[0].node_name.equals("layer_0"));
     REPORTER_ASSERT(reporter, transforms[0].handle->get() == skottie::TransformPropertyValue({
         SkPoint::Make(0, 0),
@@ -263,9 +263,18 @@
         0,
         0
     }));
-    REPORTER_ASSERT(reporter, transforms[1].node_name.equals("shape_transform_0"));
+    REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_1"));
     REPORTER_ASSERT(reporter, transforms[1].handle->get() == skottie::TransformPropertyValue({
         SkPoint::Make(0, 0),
+        SkPoint::Make(25, 25),
+        SkVector::Make(100, 100),
+        0,
+        0,
+        0
+    }));
+    REPORTER_ASSERT(reporter, transforms[2].node_name.equals("shape_transform_0"));
+    REPORTER_ASSERT(reporter, transforms[2].handle->get() == skottie::TransformPropertyValue({
+        SkPoint::Make(0, 0),
         SkPoint::Make(0, 0),
         SkVector::Make(50, 50),
         0,
diff --git a/modules/skottie/src/SkottieTool.cpp b/modules/skottie/src/SkottieTool.cpp
index b00d5d7..501290a 100644
--- a/modules/skottie/src/SkottieTool.cpp
+++ b/modules/skottie/src/SkottieTool.cpp
@@ -12,7 +12,7 @@
 #include "include/core/SkSurface.h"
 #include "include/encode/SkPngEncoder.h"
 #include "modules/skottie/include/Skottie.h"
-#include "modules/skottie/utils/SkottieUtils.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/core/SkMakeUnique.h"
 #include "src/core/SkOSFile.h"
 #include "src/core/SkTaskGroup.h"
@@ -260,9 +260,9 @@
     }
 
     auto logger = sk_make_sp<Logger>();
-    auto     rp = skottie_utils::CachingResourceProvider::Make(
-                    skottie_utils::DataURIResourceProviderProxy::Make(
-                      skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(FLAGS_input[0]),
+    auto     rp = skresources::CachingResourceProvider::Make(
+                    skresources::DataURIResourceProviderProxy::Make(
+                      skresources::FileResourceProvider::Make(SkOSPath::Dirname(FLAGS_input[0]),
                                                                 /*predecode=*/true),
                       /*predecode=*/true));
     auto data   = SkData::MakeFromFileName(FLAGS_input[0]);
diff --git a/modules/skottie/src/SkottieValue.cpp b/modules/skottie/src/SkottieValue.cpp
index 409fcb7..f98f121 100644
--- a/modules/skottie/src/SkottieValue.cpp
+++ b/modules/skottie/src/SkottieValue.cpp
@@ -62,6 +62,7 @@
     }
 }
 
+// DEPRECATED: remove after converting everything to SkColor4f
 template <>
 template <>
 SkColor ValueTraits<VectorValue>::As<SkColor>(const VectorValue& v) {
@@ -79,6 +80,18 @@
 
 template <>
 template <>
+SkColor4f ValueTraits<VectorValue>::As<SkColor4f>(const VectorValue& v) {
+    // best effort to turn a vector into a color
+    const auto r = v.size() > 0 ? SkTPin(v[0], 0.0f, 1.0f) : 0,
+               g = v.size() > 1 ? SkTPin(v[1], 0.0f, 1.0f) : 0,
+               b = v.size() > 2 ? SkTPin(v[2], 0.0f, 1.0f) : 0,
+               a = v.size() > 3 ? SkTPin(v[3], 0.0f, 1.0f) : 1;
+
+    return { r, g, b, a };
+}
+
+template <>
+template <>
 SkPoint ValueTraits<VectorValue>::As<SkPoint>(const VectorValue& vec) {
     // best effort to turn this into a point
     const auto x = vec.size() > 0 ? vec[0] : 0,
diff --git a/modules/skottie/src/effects/GradientEffect.cpp b/modules/skottie/src/effects/GradientEffect.cpp
index 2165128..5502246 100644
--- a/modules/skottie/src/effects/GradientEffect.cpp
+++ b/modules/skottie/src/effects/GradientEffect.cpp
@@ -23,12 +23,12 @@
     explicit GradientRampEffectAdapter(sk_sp<sksg::RenderNode> child)
         : fRoot(sksg::ShaderEffect::Make(std::move(child))) {}
 
-    ADAPTER_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0))
-    ADAPTER_PROPERTY(EndPoint  , SkPoint , SkPoint::Make(0, 0))
-    ADAPTER_PROPERTY(StartColor, SkColor ,       SK_ColorBLACK)
-    ADAPTER_PROPERTY(EndColor  , SkColor ,       SK_ColorBLACK)
-    ADAPTER_PROPERTY(Blend     , SkScalar,                   0)
-    ADAPTER_PROPERTY(Scatter   , SkScalar,                   0)
+    ADAPTER_PROPERTY(StartPoint, SkPoint  , SkPoint::Make(0, 0))
+    ADAPTER_PROPERTY(EndPoint  , SkPoint  , SkPoint::Make(0, 0))
+    ADAPTER_PROPERTY(StartColor, SkColor4f,    SkColors::kBlack)
+    ADAPTER_PROPERTY(EndColor  , SkColor4f,    SkColors::kBlack)
+    ADAPTER_PROPERTY(Blend     , SkScalar ,                   0)
+    ADAPTER_PROPERTY(Scatter   , SkScalar ,                   0)
 
     // Really an enum: 1 -> linear, 7 -> radial (?!)
     ADAPTER_PROPERTY(Shape     , SkScalar,                   0)
@@ -120,11 +120,11 @@
         });
     fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kStartColor_Index),
         [adapter](const VectorValue& c0) {
-            adapter->setStartColor(ValueTraits<VectorValue>::As<SkColor>(c0));
+            adapter->setStartColor(ValueTraits<VectorValue>::As<SkColor4f>(c0));
         });
     fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kEndColor_Index),
         [adapter](const VectorValue& c1) {
-            adapter->setEndColor(ValueTraits<VectorValue>::As<SkColor>(c1));
+            adapter->setEndColor(ValueTraits<VectorValue>::As<SkColor4f>(c1));
         });
     fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRampShape_Index),
         [adapter](const ScalarValue& shape) {
diff --git a/modules/skottie/src/effects/HueSaturationEffect.cpp b/modules/skottie/src/effects/HueSaturationEffect.cpp
index b636824..8dc217e 100644
--- a/modules/skottie/src/effects/HueSaturationEffect.cpp
+++ b/modules/skottie/src/effects/HueSaturationEffect.cpp
@@ -7,7 +7,6 @@
 
 #include "modules/skottie/src/effects/Effects.h"
 
-#include "include/effects/SkColorMatrix.h"
 #include "modules/skottie/src/SkottieAdapter.h"
 #include "modules/skottie/src/SkottieJson.h"
 #include "modules/skottie/src/SkottieValue.h"
diff --git a/modules/skottie/src/layers/PrecompLayer.cpp b/modules/skottie/src/layers/PrecompLayer.cpp
index eaf6ccd..21c7761 100644
--- a/modules/skottie/src/layers/PrecompLayer.cpp
+++ b/modules/skottie/src/layers/PrecompLayer.cpp
@@ -7,6 +7,7 @@
 
 #include "modules/skottie/src/SkottiePriv.h"
 
+#include "modules/skottie/src/Composition.h"
 #include "modules/skottie/src/SkottieJson.h"
 #include "modules/skottie/src/SkottieValue.h"
 #include "modules/sksg/include/SkSGRenderNode.h"
@@ -38,7 +39,7 @@
 
     auto precomp_layer = this->attachAssetRef(jlayer,
         [this] (const skjson::ObjectValue& jcomp) {
-            return this->attachComposition(jcomp);
+            return CompositionBuilder(*this, jcomp).build(*this);
         });
 
     // Applies a bias/scale/remap t-adjustment to child animators.
diff --git a/modules/skottie/src/layers/ShapeLayer.cpp b/modules/skottie/src/layers/ShapeLayer.cpp
index 3b7f98c..97a96da 100644
--- a/modules/skottie/src/layers/ShapeLayer.cpp
+++ b/modules/skottie/src/layers/ShapeLayer.cpp
@@ -41,8 +41,8 @@
 sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
                                               const AnimationBuilder* abuilder) {
     auto rect_node = sksg::RRect::Make();
-    rect_node->setDirection(ParseDefault(jrect["d"], -1) == 3 ? SkPath::kCCW_Direction
-                                                              : SkPath::kCW_Direction);
+    rect_node->setDirection(ParseDefault(jrect["d"], -1) == 3 ? SkPathDirection::kCCW
+                                                              : SkPathDirection::kCW);
     rect_node->setInitialPointIndex(2); // starting point: (Right, Top - radius.y)
 
     auto adapter = sk_make_sp<RRectAdapter>(rect_node);
@@ -70,8 +70,8 @@
 sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
                                                 const AnimationBuilder* abuilder) {
     auto rect_node = sksg::RRect::Make();
-    rect_node->setDirection(ParseDefault(jellipse["d"], -1) == 3 ? SkPath::kCCW_Direction
-                                                                 : SkPath::kCW_Direction);
+    rect_node->setDirection(ParseDefault(jellipse["d"], -1) == 3 ? SkPathDirection::kCCW
+                                                                 : SkPathDirection::kCW);
     rect_node->setInitialPointIndex(1); // starting point: (Center, Top)
 
     auto adapter = sk_make_sp<RRectAdapter>(rect_node);
@@ -169,7 +169,7 @@
 
     abuilder->bindProperty<VectorValue>((*stops)["k"],
         [adapter](const VectorValue& stops) {
-            adapter->setColorStops(stops);
+            adapter->setStops(stops);
         });
     abuilder->bindProperty<VectorValue>(jgrad["s"],
         [adapter](const VectorValue& s) {
@@ -292,15 +292,15 @@
         std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
 
     enum class Mode {
-        kMerged,   // "m": 1
-        kSeparate, // "m": 2
-    } gModes[] = { Mode::kMerged, Mode::kSeparate };
+        kParallel, // "m": 1 (Trim Multiple Shapes: Simultaneously)
+        kSerial,   // "m": 2 (Trim Multiple Shapes: Individually)
+    } gModes[] = { Mode::kParallel, Mode::kSerial};
 
     const auto mode = gModes[SkTMin<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
                                             SK_ARRAY_COUNT(gModes) - 1)];
 
     std::vector<sk_sp<sksg::GeometryNode>> inputs;
-    if (mode == Mode::kMerged) {
+    if (mode == Mode::kSerial) {
         inputs.push_back(Merge(std::move(geos), sksg::Merge::Mode::kMerge));
     } else {
         inputs = std::move(geos);
diff --git a/modules/skottie/utils/SkottieUtils.cpp b/modules/skottie/utils/SkottieUtils.cpp
index 7b750bb..f8d7d6d 100644
--- a/modules/skottie/utils/SkottieUtils.cpp
+++ b/modules/skottie/utils/SkottieUtils.cpp
@@ -7,181 +7,8 @@
 
 #include "modules/skottie/utils/SkottieUtils.h"
 
-#include "include/codec/SkCodec.h"
-#include "include/core/SkData.h"
-#include "include/core/SkImage.h"
-#include "include/utils/SkAnimCodecPlayer.h"
-#include "include/utils/SkBase64.h"
-#include "src/core/SkMakeUnique.h"
-#include "src/core/SkOSFile.h"
-#include "src/utils/SkOSPath.h"
-
-#include <cmath>
-
 namespace skottie_utils {
 
-sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(sk_sp<SkData> data, bool predecode) {
-    if (auto codec = SkCodec::MakeFromData(std::move(data))) {
-        return sk_sp<MultiFrameImageAsset>(
-              new MultiFrameImageAsset(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec)),
-                                                                             predecode));
-    }
-
-    return nullptr;
-}
-
-MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player,
-                                           bool predecode)
-    : fPlayer(std::move(player))
-    , fPreDecode(predecode) {
-    SkASSERT(fPlayer);
-}
-
-bool MultiFrameImageAsset::isMultiFrame() {
-    return fPlayer->duration() > 0;
-}
-
-sk_sp<SkImage> MultiFrameImageAsset::getFrame(float t) {
-    auto decode = [](sk_sp<SkImage> image) {
-        SkASSERT(image->isLazyGenerated());
-
-        static constexpr size_t kMaxArea = 2048 * 2048;
-        const auto image_area = SkToSizeT(image->width() * image->height());
-
-        if (image_area > kMaxArea) {
-            // When the image is too large, decode and scale down to a reasonable size.
-            const auto scale = std::sqrt(static_cast<float>(kMaxArea) / image_area);
-            const auto info  = SkImageInfo::MakeN32Premul(scale * image->width(),
-                                                          scale * image->height());
-            SkBitmap bm;
-            if (bm.tryAllocPixels(info, info.minRowBytes()) &&
-                    image->scalePixels(bm.pixmap(),
-                                       SkFilterQuality::kMedium_SkFilterQuality,
-                                       SkImage::kDisallow_CachingHint)) {
-                image = SkImage::MakeFromBitmap(bm);
-            }
-        } else {
-            // When the image size is OK, just force-decode.
-            image = image->makeRasterImage();
-        }
-
-        return image;
-    };
-
-    fPlayer->seek(static_cast<uint32_t>(t * 1000));
-    auto frame = fPlayer->getFrame();
-
-    if (fPreDecode && frame && frame->isLazyGenerated()) {
-        frame = decode(std::move(frame));
-    }
-
-    return frame;
-}
-
-sk_sp<FileResourceProvider> FileResourceProvider::Make(SkString base_dir, bool predecode) {
-    return sk_isdir(base_dir.c_str())
-        ? sk_sp<FileResourceProvider>(new FileResourceProvider(std::move(base_dir), predecode))
-        : nullptr;
-}
-
-FileResourceProvider::FileResourceProvider(SkString base_dir, bool predecode)
-    : fDir(std::move(base_dir))
-    , fPredecode(predecode) {}
-
-sk_sp<SkData> FileResourceProvider::load(const char resource_path[],
-                                         const char resource_name[]) const {
-    const auto full_dir  = SkOSPath::Join(fDir.c_str()    , resource_path),
-               full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
-    return SkData::MakeFromFileName(full_path.c_str());
-}
-
-sk_sp<skottie::ImageAsset> FileResourceProvider::loadImageAsset(const char resource_path[],
-                                                                const char resource_name[],
-                                                                const char[]) const {
-    return MultiFrameImageAsset::Make(this->load(resource_path, resource_name), fPredecode);
-}
-
-ResourceProviderProxyBase::ResourceProviderProxyBase(sk_sp<ResourceProvider> rp)
-    : fProxy(std::move(rp)) {}
-
-sk_sp<SkData> ResourceProviderProxyBase::load(const char resource_path[],
-                                              const char resource_name[]) const {
-    return fProxy ? fProxy->load(resource_path, resource_name)
-                  : nullptr;
-}
-
-sk_sp<skottie::ImageAsset> ResourceProviderProxyBase::loadImageAsset(const char rpath[],
-                                                                     const char rname[],
-                                                                     const char rid[]) const {
-    return fProxy ? fProxy->loadImageAsset(rpath, rname, rid)
-                  : nullptr;
-}
-
-sk_sp<SkData> ResourceProviderProxyBase::loadFont(const char name[], const char url[]) const {
-    return fProxy ? fProxy->loadFont(name, url)
-                  : nullptr;
-}
-
-CachingResourceProvider::CachingResourceProvider(sk_sp<ResourceProvider> rp)
-    : INHERITED(std::move(rp)) {}
-
-sk_sp<skottie::ImageAsset> CachingResourceProvider::loadImageAsset(const char resource_path[],
-                                                                   const char resource_name[],
-                                                                   const char resource_id[]) const {
-    SkAutoMutexExclusive amx(fMutex);
-
-    const SkString key(resource_id);
-    if (const auto* asset = fImageCache.find(key)) {
-        return *asset;
-    }
-
-    auto asset = this->INHERITED::loadImageAsset(resource_path, resource_name, resource_id);
-    fImageCache.set(key, asset);
-
-    return asset;
-}
-
-sk_sp<DataURIResourceProviderProxy> DataURIResourceProviderProxy::Make(sk_sp<ResourceProvider> rp,
-                                                                       bool predecode) {
-    return sk_sp<DataURIResourceProviderProxy>(
-            new DataURIResourceProviderProxy(std::move(rp), predecode));
-}
-
-DataURIResourceProviderProxy::DataURIResourceProviderProxy(sk_sp<ResourceProvider> rp,
-                                                           bool predecode)
-    : INHERITED(std::move(rp))
-    , fPredecode(predecode) {}
-
-sk_sp<skottie::ImageAsset> DataURIResourceProviderProxy::loadImageAsset(const char rpath[],
-                                                                        const char rname[],
-                                                                        const char rid[]) const {
-    // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
-    // (https://en.wikipedia.org/wiki/Data_URI_scheme)
-    static constexpr char kDataURIImagePrefix[] = "data:image/",
-                          kDataURIEncodingStr[] = ";base64,";
-
-    if (!strncmp(rname, kDataURIImagePrefix, SK_ARRAY_COUNT(kDataURIImagePrefix) - 1)) {
-        const char* encoding_start = strstr(rname + SK_ARRAY_COUNT(kDataURIImagePrefix) - 1,
-                                            kDataURIEncodingStr);
-        if (encoding_start) {
-            const char* data_start = encoding_start + SK_ARRAY_COUNT(kDataURIEncodingStr) - 1;
-
-            // TODO: SkBase64::decode ergonomics are... interesting.
-            SkBase64 b64;
-            if (SkBase64::kNoError == b64.decode(data_start, strlen(data_start))) {
-                return MultiFrameImageAsset::Make(SkData::MakeWithProc(b64.getData(),
-                                                                       b64.getDataSize(),
-                                                      [](const void* ptr, void*) {
-                                                          delete[] static_cast<const char*>(ptr);
-                                                      }, /*ctx=*/nullptr),
-                                                  fPredecode);
-            }
-        }
-    }
-
-    return this->INHERITED::loadImageAsset(rpath, rname, rid);
-}
-
 class CustomPropertyManager::PropertyInterceptor final : public skottie::PropertyObserver {
 public:
     explicit PropertyInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
diff --git a/modules/skottie/utils/SkottieUtils.h b/modules/skottie/utils/SkottieUtils.h
index 7933ed5..a64ae14 100644
--- a/modules/skottie/utils/SkottieUtils.h
+++ b/modules/skottie/utils/SkottieUtils.h
@@ -8,10 +8,6 @@
 #ifndef SkottieUtils_DEFINED
 #define SkottieUtils_DEFINED
 
-#include "include/core/SkColor.h"
-#include "include/core/SkString.h"
-#include "include/private/SkMutex.h"
-#include "include/private/SkTHash.h"
 #include "modules/skottie/include/Skottie.h"
 #include "modules/skottie/include/SkottieProperty.h"
 
@@ -20,102 +16,8 @@
 #include <unordered_map>
 #include <vector>
 
-class SkAnimCodecPlayer;
-class SkData;
-class SkImage;
-
 namespace skottie_utils {
 
-class MultiFrameImageAsset final : public skottie::ImageAsset {
-public:
-    /**
-    * By default, images are decoded on-the-fly, at rasterization time.
-    * Large images may cause jank as decoding is expensive (and can thrash internal caches).
-    *
-    * Pass |predecode| true to force-decode all images upfront, at the cost of potentially more RAM
-    * and slower animation build times.
-    */
-    static sk_sp<MultiFrameImageAsset> Make(sk_sp<SkData>, bool predecode = false);
-
-    bool isMultiFrame() override;
-
-    sk_sp<SkImage> getFrame(float t) override;
-
-private:
-    explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>, bool predecode);
-
-    std::unique_ptr<SkAnimCodecPlayer> fPlayer;
-    bool                               fPreDecode;
-
-    using INHERITED = skottie::ImageAsset;
-};
-
-class FileResourceProvider final : public skottie::ResourceProvider {
-public:
-    static sk_sp<FileResourceProvider> Make(SkString base_dir, bool predecode = false);
-
-    sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
-
-    sk_sp<skottie::ImageAsset> loadImageAsset(const char[], const char[],
-                                              const char[]) const override;
-
-private:
-    FileResourceProvider(SkString, bool);
-
-    const SkString fDir;
-    const bool     fPredecode;
-
-    using INHERITED = skottie::ResourceProvider;
-};
-
-class ResourceProviderProxyBase : public skottie::ResourceProvider {
-protected:
-    explicit ResourceProviderProxyBase(sk_sp<ResourceProvider>);
-
-    sk_sp<SkData> load(const char[], const char[]) const override;
-    sk_sp<skottie::ImageAsset> loadImageAsset(const char[], const char[],
-                                              const char[]) const override;
-    sk_sp<SkData> loadFont(const char[], const char[]) const override;
-
-private:
-    const sk_sp<ResourceProvider> fProxy;
-};
-
-class CachingResourceProvider final : public ResourceProviderProxyBase {
-public:
-    static sk_sp<CachingResourceProvider> Make(sk_sp<ResourceProvider> rp) {
-        return rp ? sk_sp<CachingResourceProvider>(new CachingResourceProvider(std::move(rp)))
-                  : nullptr;
-    }
-
-private:
-    explicit CachingResourceProvider(sk_sp<ResourceProvider>);
-
-    sk_sp<skottie::ImageAsset> loadImageAsset(const char[], const char[],
-                                              const char[]) const override;
-
-    mutable SkMutex                                          fMutex;
-    mutable SkTHashMap<SkString, sk_sp<skottie::ImageAsset>> fImageCache;
-
-    using INHERITED = ResourceProviderProxyBase;
-};
-
-class DataURIResourceProviderProxy final : public ResourceProviderProxyBase {
-public:
-    static sk_sp<DataURIResourceProviderProxy> Make(sk_sp<ResourceProvider> rp,
-                                                    bool predecode = false);
-
-private:
-    DataURIResourceProviderProxy(sk_sp<ResourceProvider>, bool);
-
-    sk_sp<skottie::ImageAsset> loadImageAsset(const char[], const char[],
-                                              const char[]) const override;
-
-    const bool fPredecode;
-
-    using INHERITED = ResourceProviderProxyBase;
-};
-
 /**
  * CustomPropertyManager implements a property management scheme where color/opacity/transform
  * attributes are grouped and manipulated by name (one-to-many mapping).
diff --git a/modules/skparagraph/BUILD.gn b/modules/skparagraph/BUILD.gn
index 5a85d37..80b86e0 100644
--- a/modules/skparagraph/BUILD.gn
+++ b/modules/skparagraph/BUILD.gn
@@ -3,7 +3,7 @@
 import("../../gn/skia.gni")
 
 declare_args() {
-  skia_enable_skparagraph = !(is_win && is_component_build)
+  skia_enable_skparagraph = true
   paragraph_tests_enabled = true
   paragraph_bench_enabled = false
 }
diff --git a/modules/skparagraph/include/FontCollection.h b/modules/skparagraph/include/FontCollection.h
index 22d89ed..f733628 100644
--- a/modules/skparagraph/include/FontCollection.h
+++ b/modules/skparagraph/include/FontCollection.h
@@ -31,12 +31,13 @@
 
     sk_sp<SkFontMgr> getFallbackManager() const { return fDefaultFontManager; }
 
-    sk_sp<SkTypeface> matchTypeface(const char familyName[], SkFontStyle fontStyle, const SkString& locale);
-    sk_sp<SkTypeface> matchDefaultTypeface(SkFontStyle fontStyle, const SkString& locale);
+    std::vector<sk_sp<SkTypeface>> findTypefaces(const std::vector<SkString>& familyNames, SkFontStyle fontStyle);
+
     sk_sp<SkTypeface> defaultFallback(SkUnichar unicode, SkFontStyle fontStyle, const SkString& locale);
     sk_sp<SkTypeface> defaultFallback();
 
     void disableFontFallback();
+    void enableFontFallback();
     bool fontFallbackEnabled() { return fEnableFontFallback; }
 
     ParagraphCache* getParagraphCache() { return &fParagraphCache; }
@@ -44,14 +45,15 @@
 private:
     std::vector<sk_sp<SkFontMgr>> getFontManagerOrder() const;
 
+    sk_sp<SkTypeface> matchTypeface(const SkString& familyName, SkFontStyle fontStyle);
+
     struct FamilyKey {
-        FamilyKey(const char family[], const char loc[], SkFontStyle style)
-                : fFontFamily(family), fLocale(loc), fFontStyle(style) {}
+        FamilyKey(const std::vector<SkString>& familyNames, SkFontStyle style)
+                : fFamilyNames(familyNames), fFontStyle(style) {}
 
         FamilyKey() {}
 
-        SkString fFontFamily;
-        SkString fLocale;
+        std::vector<SkString> fFamilyNames;
         SkFontStyle fFontStyle;
 
         bool operator==(const FamilyKey& other) const;
@@ -62,7 +64,7 @@
     };
 
     bool fEnableFontFallback;
-    SkTHashMap<FamilyKey, sk_sp<SkTypeface>, FamilyKey::Hasher> fTypefaces;
+    SkTHashMap<FamilyKey, std::vector<sk_sp<SkTypeface>>, FamilyKey::Hasher> fTypefaces;
     sk_sp<SkFontMgr> fDefaultFontManager;
     sk_sp<SkFontMgr> fAssetFontManager;
     sk_sp<SkFontMgr> fDynamicFontManager;
diff --git a/modules/skparagraph/include/Paragraph.h b/modules/skparagraph/include/Paragraph.h
index dffcb40..d262aae 100644
--- a/modules/skparagraph/include/Paragraph.h
+++ b/modules/skparagraph/include/Paragraph.h
@@ -63,6 +63,10 @@
 
     virtual void markDirty() = 0;
 
+    // This function will return the number of unresolved glyphs or
+    // -1 if not applicable (has not been shaped yet - valid case)
+    virtual int32_t unresolvedGlyphs() = 0;
+
     // Experimental API that allows fast way to update "immutable" paragraph
     virtual void updateTextAlign(TextAlign textAlign) = 0;
     virtual void updateText(size_t from, SkString text) = 0;
diff --git a/modules/skparagraph/include/ParagraphCache.h b/modules/skparagraph/include/ParagraphCache.h
index ac4b4f1..0bfe59f 100644
--- a/modules/skparagraph/include/ParagraphCache.h
+++ b/modules/skparagraph/include/ParagraphCache.h
@@ -11,16 +11,6 @@
 namespace skia {
 namespace textlayout {
 
-struct Measurement {
-    SkScalar fAlphabeticBaseline;
-    SkScalar fIdeographicBaseline;
-    SkScalar fHeight;
-    SkScalar fWidth;
-    SkScalar fMaxIntrinsicWidth;
-    SkScalar fMinIntrinsicWidth;
-    SkScalar fLongestLine;
-};
-
 enum InternalState {
   kUnknown = 0,
   kShaped = 1,
diff --git a/modules/skparagraph/skparagraph.gni b/modules/skparagraph/skparagraph.gni
index f127d68..c5bcd47 100644
--- a/modules/skparagraph/skparagraph.gni
+++ b/modules/skparagraph/skparagraph.gni
@@ -20,9 +20,9 @@
 
 skparagraph_sources = [
   "$_src/FontCollection.cpp",
-  "$_src/FontResolver.h",
-  "$_src/FontResolver.cpp",
   "$_src/Iterators.h",
+  "$_src/OneLineShaper.h",
+  "$_src/OneLineShaper.cpp",
   "$_src/ParagraphBuilderImpl.h",
   "$_src/ParagraphBuilderImpl.cpp",
   "$_src/ParagraphCache.cpp",
diff --git a/modules/skparagraph/src/FontCollection.cpp b/modules/skparagraph/src/FontCollection.cpp
index bb0d60c..ec47018 100644
--- a/modules/skparagraph/src/FontCollection.cpp
+++ b/modules/skparagraph/src/FontCollection.cpp
@@ -8,13 +8,15 @@
 namespace textlayout {
 
 bool FontCollection::FamilyKey::operator==(const FontCollection::FamilyKey& other) const {
-    return fFontFamily == other.fFontFamily && fLocale == other.fLocale &&
-           fFontStyle == other.fFontStyle;
+    return fFamilyNames == other.fFamilyNames && fFontStyle == other.fFontStyle;
 }
 
 size_t FontCollection::FamilyKey::Hasher::operator()(const FontCollection::FamilyKey& key) const {
-    return std::hash<std::string>()(key.fFontFamily.c_str()) ^
-           std::hash<std::string>()(key.fLocale.c_str()) ^
+    size_t hash = 0;
+    for (const SkString& family : key.fFamilyNames) {
+        hash ^= std::hash<std::string>()(family.c_str());
+    }
+    return hash ^
            std::hash<uint32_t>()(key.fFontStyle.weight()) ^
            std::hash<uint32_t>()(key.fFontStyle.slant());
 }
@@ -65,49 +67,36 @@
     return order;
 }
 
-sk_sp<SkTypeface> FontCollection::matchTypeface(const char familyName[], SkFontStyle fontStyle, const SkString& locale) {
+std::vector<sk_sp<SkTypeface>> FontCollection::findTypefaces(const std::vector<SkString>& familyNames, SkFontStyle fontStyle) {
     // Look inside the font collections cache first
-    FamilyKey familyKey(familyName, locale.c_str(), fontStyle);
+    FamilyKey familyKey(familyNames, fontStyle);
     auto found = fTypefaces.find(familyKey);
     if (found) {
         return *found;
     }
 
-    sk_sp<SkTypeface> typeface = nullptr;
-    for (const auto& manager : this->getFontManagerOrder()) {
-        SkFontStyleSet* set = manager->matchFamily( manager == fTestFontManager ? "Ahem" : familyName);
-        if (nullptr == set || set->count() == 0) {
-            continue;
-        }
-
-        for (int i = 0; i < set->count(); ++i) {
-            set->createTypeface(i);
-        }
-
-        sk_sp<SkTypeface> match(set->matchStyle(fontStyle));
-        if (match || manager == fTestFontManager) {
-            typeface = std::move(match);
-            break;
+    std::vector<sk_sp<SkTypeface>> typefaces;
+    for (const SkString& familyName : familyNames) {
+        sk_sp<SkTypeface> match = matchTypeface(familyName, fontStyle);
+        if (match) {
+            typefaces.emplace_back(std::move(match));
         }
     }
 
-    if (typeface != nullptr && typeface.get() != nullptr) {
-        fTypefaces.set(familyKey, typeface);
+    if (typefaces.empty()) {
+        sk_sp<SkTypeface> match = matchTypeface(fDefaultFamilyName, fontStyle);
+        if (match) {
+            typefaces.emplace_back(std::move(match));
+        }
     }
-    return typeface;
+
+    fTypefaces.set(familyKey, typefaces);
+    return typefaces;
 }
 
-sk_sp<SkTypeface> FontCollection::matchDefaultTypeface(SkFontStyle fontStyle, const SkString& locale) {
-    // Look inside the font collections cache first
-    FamilyKey familyKey(fDefaultFamilyName.c_str(), locale.c_str(), fontStyle);
-    auto found = fTypefaces.find(familyKey);
-    if (found) {
-        return *found;
-    }
-
-    sk_sp<SkTypeface> typeface = nullptr;
+sk_sp<SkTypeface> FontCollection::matchTypeface(const SkString& familyName, SkFontStyle fontStyle) {
     for (const auto& manager : this->getFontManagerOrder()) {
-        SkFontStyleSet* set = manager->matchFamily(fDefaultFamilyName.c_str());
+        SkFontStyleSet* set = manager->matchFamily(familyName.c_str());
         if (nullptr == set || set->count() == 0) {
             continue;
         }
@@ -117,16 +106,12 @@
         }
 
         sk_sp<SkTypeface> match(set->matchStyle(fontStyle));
-        if (match || manager == fTestFontManager) {
-            typeface = std::move(match);
-            break;
+        if (match) {
+            return match;
         }
     }
 
-    if (typeface != nullptr && typeface.get() != nullptr) {
-        fTypefaces.set(familyKey, typeface);
-    }
-    return typeface;
+    return nullptr;
 }
 
 // Find ANY font in available font managers that resolves the unicode codepoint
@@ -156,6 +141,7 @@
 
 
 void FontCollection::disableFontFallback() { fEnableFontFallback = false; }
+void FontCollection::enableFontFallback() { fEnableFontFallback = true; }
 
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/src/FontResolver.cpp b/modules/skparagraph/src/FontResolver.cpp
deleted file mode 100644
index ac02f98..0000000
--- a/modules/skparagraph/src/FontResolver.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-// Copyright 2019 Google LLC.
-
-#include <unicode/brkiter.h>
-#include <unicode/ubidi.h>
-#include "include/core/SkBlurTypes.h"
-#include "include/core/SkCanvas.h"
-#include "include/core/SkFontMgr.h"
-#include "include/core/SkPictureRecorder.h"
-#include "modules/skparagraph/src/FontResolver.h"
-#include "modules/skparagraph/src/ParagraphImpl.h"
-#include "src/core/SkSpan.h"
-#include "src/utils/SkUTF.h"
-
-namespace {
-SkUnichar utf8_next(const char** ptr, const char* end) {
-    SkUnichar val = SkUTF::NextUTF8(ptr, end);
-    return val < 0 ? 0xFFFD : val;
-}
-}  // namespace
-
-// TODO: FontResolver and FontIterator have common functionality
-namespace skia {
-namespace textlayout {
-
-bool FontResolver::findNext(const char* codepoint, SkFont* font, SkScalar* height) {
-
-    SkASSERT(fFontIterator != nullptr);
-    TextIndex index = codepoint - fText.begin();
-    while (fFontIterator != fFontSwitches.end() && fFontIterator->fStart <= index) {
-        if (fFontIterator->fStart == index) {
-            *font = fFontIterator->fFont;
-            *height = fFontIterator->fHeight;
-            return true;
-        }
-        ++fFontIterator;
-    }
-    return false;
-}
-
-bool FontResolver::isEmpty() {
-    return fFontIterator == fFontSwitches.end();
-}
-
-void FontResolver::getFirstFont(SkFont* font, SkScalar* height) {
-    *font = fFirstResolvedFont.fFont;
-    *height = fFirstResolvedFont.fHeight;
-}
-
-void FontResolver::findAllFontsForStyledBlock(const TextStyle& style, TextRange textRange) {
-    fCodepoints.reset();
-    fCharacters.reset();
-    fUnresolvedIndexes.reset();
-    fUnresolvedCodepoints.reset();
-
-    // Extract all unicode codepoints
-    const char* end = fText.begin() + textRange.end;
-    const char* current = fText.begin() + textRange.start;
-    while (current != end) {
-        fCharacters.emplace_back(current);
-        fCodepoints.emplace_back(utf8_next(&current, end));
-        fUnresolvedIndexes.emplace_back(fUnresolvedIndexes.size());
-    }
-    fUnresolvedCodepoints.push_back_n(fUnresolvedIndexes.size());
-    fUnresolved = fCodepoints.size();
-
-    // Walk through all available fonts to resolve the block
-    auto wasUnresolved = fUnresolved;
-    for (auto& fontFamily : style.getFontFamilies()) {
-        auto typeface = fFontCollection->matchTypeface(fontFamily.c_str(), style.getFontStyle(), style.getLocale());
-        if (typeface.get() == nullptr) {
-            continue;
-        }
-
-        // Resolve all unresolved characters
-        auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
-        resolveAllCharactersByFont(font);
-        if (fUnresolved == 0) {
-            break;
-        }
-    }
-
-    if (fUnresolved != wasUnresolved || allWhitespaces()) {
-        addResolvedWhitespacesToMapping();
-        wasUnresolved = fUnresolved;
-    }
-
-    if (fUnresolved > 0) {
-        // Check the default font
-        auto typeface =
-                fFontCollection->matchDefaultTypeface(style.getFontStyle(), style.getLocale());
-        if (typeface != nullptr) {
-            auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
-            resolveAllCharactersByFont(font);
-        }
-        if (fUnresolved != wasUnresolved || allWhitespaces()) {
-            addResolvedWhitespacesToMapping();
-            wasUnresolved = fUnresolved;
-        }
-    }
-
-    if (fUnresolved > 0 && fFontCollection->fontFallbackEnabled()) {
-        while (fUnresolved > 0 && !allWhitespaces()) {
-            auto unicode = firstUnresolved();
-            auto typeface = fFontCollection->defaultFallback(unicode, style.getFontStyle(), style.getLocale());
-            if (typeface == nullptr) {
-                break;
-            }
-
-            SkString name;
-            typeface->getFamilyName(&name);
-            auto font = makeFont(typeface, style.getFontSize(), style.getHeight());
-            auto newResolved = resolveAllCharactersByFont(font);
-            if (newResolved == 0) {
-                // Not a single unicode character was resolved
-                break;
-            }
-        }
-        if (fUnresolved != wasUnresolved || allWhitespaces()) {
-            addResolvedWhitespacesToMapping();
-            wasUnresolved = fUnresolved;
-        }
-    }
-}
-
-size_t FontResolver::resolveAllCharactersByFont(const FontDescr& font) {
-    // Consolidate all unresolved unicodes in one array to make a batch call
-    SkTArray<SkGlyphID> glyphs(fUnresolved);
-    glyphs.push_back_n(fUnresolved, SkGlyphID(0));
-    font.fFont.getTypeface()->unicharsToGlyphs(
-            fUnresolved == fCodepoints.size() ? fCodepoints.data() : fUnresolvedCodepoints.data(),
-            fUnresolved, glyphs.data());
-
-    SkRange<size_t> resolved(0, 0);
-    SkRange<size_t> whitespaces(0, 0);
-    size_t stillUnresolved = 0;
-
-    auto processRuns = [&]() {
-        if (resolved.width() == 0) {
-            return;
-        }
-
-        if (resolved.width() == whitespaces.width()) {
-            // The entire run is just whitespaces;
-            // Remember the font and mark whitespaces back unresolved
-            // to calculate its mapping for the other fonts
-            for (auto w = whitespaces.start; w != whitespaces.end; ++w) {
-                if (fWhitespaces.find(w) == nullptr) {
-                    fWhitespaces.set(w, font);
-                }
-                fUnresolvedIndexes[stillUnresolved] = w;
-                fUnresolvedCodepoints[stillUnresolved] = fCodepoints[w];
-                ++stillUnresolved;
-            }
-        } else {
-            fFontMapping.set(fCharacters[resolved.start] - fText.begin(), font);
-        }
-    };
-
-    // Try to resolve all the unresolved unicode points
-    SkString name;
-    font.fFont.getTypeface()->getFamilyName(&name);
-    for (size_t i = 0; i < glyphs.size(); ++i) {
-        auto glyph = glyphs[i];
-        auto index = fUnresolvedIndexes[i];
-        auto codepoint = fCodepoints[index];
-
-        if (u_hasBinaryProperty(codepoint, UCHAR_BIDI_CONTROL)) {
-            // Skip control characters - they don't have to be resolved
-        } else if (glyph == 0) {
-            processRuns();
-
-            resolved = SkRange<size_t>(0, 0);
-            whitespaces = SkRange<size_t>(0, 0);
-
-            fUnresolvedIndexes[stillUnresolved] = index;
-            fUnresolvedCodepoints[stillUnresolved] = codepoint;
-            ++stillUnresolved;
-            continue;
-        }
-
-        if (index == resolved.end) {
-            ++resolved.end;
-        } else {
-            processRuns();
-            resolved = SkRange<size_t>(index, index + 1);
-        }
-        if (u_isUWhiteSpace(codepoint)) {
-            if (index == whitespaces.end) {
-                ++whitespaces.end;
-            } else {
-                whitespaces = SkRange<size_t>(index, index + 1);
-            }
-        } else {
-            whitespaces = SkRange<size_t>(0, 0);
-        }
-    }
-
-    // One last time to take care of the tail run
-    processRuns();
-
-    size_t wasUnresolved = fUnresolved;
-    fUnresolved = stillUnresolved;
-    return wasUnresolved - stillUnresolved;
-}
-
-void FontResolver::addResolvedWhitespacesToMapping() {
-    size_t resolvedWhitespaces = 0;
-    for (size_t i = 0; i < fUnresolved; ++i) {
-        auto index = fUnresolvedIndexes[i];
-        auto found = fWhitespaces.find(index);
-        if (found != nullptr) {
-            fFontMapping.set(fCharacters[index] - fText.begin(), *found);
-            ++resolvedWhitespaces;
-        }
-    }
-    fUnresolved -= resolvedWhitespaces;
-}
-
-FontDescr FontResolver::makeFont(sk_sp<SkTypeface> typeface, SkScalar size, SkScalar height) {
-    SkFont font(typeface, size);
-    font.setEdging(SkFont::Edging::kAntiAlias);
-    font.setHinting(SkFontHinting::kSlight);
-    font.setSubpixel(true);
-    FontDescr descr(font, height);
-
-    const FontDescr* foundFont = fResolvedFonts.find(descr);
-    if (foundFont == nullptr) {
-        if (fResolvedFonts.count() == 0) {
-            fFirstResolvedFont = descr;
-        }
-        fResolvedFonts.add(descr);
-    }
-    return descr;
-}
-
-SkUnichar FontResolver::firstUnresolved() {
-    if (fUnresolved == 0) return 0;
-    return fUnresolvedCodepoints[0];
-}
-
-void FontResolver::setLastResortFont() {
-    TextStyle foundStyle;
-    sk_sp<SkTypeface> typeface = nullptr;
-    for (auto& style : fStyles) {
-        for (auto& fontFamily : style.fStyle.getFontFamilies()) {
-            typeface = fFontCollection->matchTypeface(fontFamily.c_str(), style.fStyle.getFontStyle(), style.fStyle.getLocale());
-            if (typeface.get() != nullptr) {
-                foundStyle = style.fStyle;
-                break;
-            }
-        }
-        if (typeface != nullptr) {
-          break;
-        }
-    }
-    if (typeface == nullptr) {
-        for (auto& fontFamily : fDefaultStyle.getFontFamilies()) {
-            typeface = fFontCollection->matchTypeface(fontFamily.c_str(), fDefaultStyle.getFontStyle(), fDefaultStyle.getLocale());
-            if (typeface.get() != nullptr) {
-                foundStyle = fDefaultStyle;
-                break;
-            }
-        }
-    }
-
-    if (typeface == nullptr) {
-        foundStyle = fStyles.empty() ? fDefaultStyle : fStyles.front().fStyle;
-        typeface = fFontCollection->defaultFallback(0, foundStyle.getFontStyle(), foundStyle.getLocale());
-    }
-
-    if (typeface == nullptr) {
-        typeface = fFontCollection->defaultFallback();
-    }
-
-    fFirstResolvedFont = makeFont(typeface, foundStyle.getFontSize(), foundStyle.getHeight());
-    fFirstResolvedFont.fStart = 0;
-}
-
-void FontResolver::findAllFontsForAllStyledBlocks(ParagraphImpl* master) {
-    fFontCollection = master->fontCollection();
-    fStyles = master->styles();
-    fText = master->text();
-    fDefaultStyle = master->paragraphStyle().getTextStyle();
-
-    if (fText.empty()) {
-        setLastResortFont();
-        return;
-    }
-
-    Block combinedBlock;
-    for (auto& block : fStyles) {
-        SkASSERT(combinedBlock.fRange.width() == 0 ||
-                 combinedBlock.fRange.end == block.fRange.start);
-
-        if (!combinedBlock.fRange.empty()) {
-            if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
-                combinedBlock.add(block.fRange);
-                continue;
-            }
-            // Resolve all characters in the block for this style
-            this->findAllFontsForStyledBlock(combinedBlock.fStyle, combinedBlock.fRange);
-        }
-
-        if (block.fStyle.isPlaceholder()) {
-            fFontMapping.set(block.fRange.start, FontDescr());
-            combinedBlock.fRange = EMPTY_RANGE;
-        } else {
-            combinedBlock.fRange = block.fRange;
-            combinedBlock.fStyle = block.fStyle;
-        }
-    }
-
-    this->findAllFontsForStyledBlock(combinedBlock.fStyle, combinedBlock.fRange);
-
-    fFontSwitches.reset();
-    FontDescr* prev = nullptr;
-    for (auto& ch : fText) {
-        if (fFontSwitches.count() == fFontMapping.count()) {
-            // Checked all
-            break;
-        }
-        auto found = fFontMapping.find(&ch - fText.begin());
-        if (found == nullptr) {
-            // Insignificant character
-            continue;
-        }
-        if (prev == nullptr) {
-            prev = found;
-            prev->fStart = 0;
-        }
-
-        if (*prev == *found) {
-            continue;
-        }
-
-        if (prev->fFont.getTypeface() != nullptr) {
-            fFontSwitches.emplace_back(*prev);
-        }
-        prev = found;
-        prev->fStart = &ch - fText.begin();
-    }
-
-    if (prev != nullptr) {
-        if (prev->fFont.getTypeface() != nullptr) {
-            fFontSwitches.emplace_back(*prev);
-        }
-    }
-
-    if (fFontSwitches.empty()) {
-        setLastResortFont();
-        if (fFirstResolvedFont.fFont.getTypeface() != nullptr) {
-            fFontSwitches.emplace_back(fFirstResolvedFont);
-        }
-    }
-
-    fFontIterator = fFontSwitches.begin();
-}
-}  // namespace textlayout
-}  // namespace skia
diff --git a/modules/skparagraph/src/FontResolver.h b/modules/skparagraph/src/FontResolver.h
deleted file mode 100644
index c178079..0000000
--- a/modules/skparagraph/src/FontResolver.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 Google LLC.
-#ifndef FontResolver_DEFINED
-#define FontResolver_DEFINED
-
-#include <memory>
-#include <set>
-#include "include/core/SkFontMgr.h"
-#include "include/core/SkRefCnt.h"
-#include "include/private/SkTHash.h"
-#include "modules/skparagraph/include/FontCollection.h"
-#include "modules/skparagraph/include/TextStyle.h"
-#include "modules/skparagraph/src/TextLine.h"
-#include "src/core/SkSpan.h"
-
-namespace skia {
-namespace textlayout {
-
-struct FontDescr {
-    FontDescr() { }
-    FontDescr(SkFont font, SkScalar height)
-            : fFont(font), fHeight(height), fStart(EMPTY_INDEX) { }
-    bool operator==(const FontDescr& a) const {
-        return this->fFont == a.fFont && this->fHeight == a.fHeight;
-    }
-    SkFont fFont;
-    SkScalar fHeight;
-    TextIndex fStart;
-};
-
-class FontResolver {
-public:
-
-    FontResolver() = default;
-    ~FontResolver() = default;
-
-    void findAllFontsForAllStyledBlocks(ParagraphImpl* master);
-    bool findNext(const char* codepoint, SkFont* font, SkScalar* height);
-    void getFirstFont(SkFont* font, SkScalar* height);
-
-    const SkTArray<FontDescr>& switches() const { return fFontSwitches; }
-
-    bool isEmpty();
-
-    bool allWhitespaces() const { return fUnresolved == SkToU32(fWhitespaces.count()); }
-
-private:
-    void findAllFontsForStyledBlock(const TextStyle& style, TextRange textRange);
-    FontDescr makeFont(sk_sp<SkTypeface> typeface, SkScalar size, SkScalar height);
-    size_t resolveAllCharactersByFont(const FontDescr& fontDescr);
-    void addResolvedWhitespacesToMapping();
-    void setLastResortFont();
-
-    struct Hash {
-        uint32_t operator()(const FontDescr& key) const {
-            return SkTypeface::UniqueID(key.fFont.getTypeface()) +
-                   SkScalarCeilToInt(key.fFont.getSize()) +
-                   SkScalarCeilToInt(key.fHeight);
-        }
-    };
-
-    SkUnichar firstUnresolved();
-
-    sk_sp<FontCollection> fFontCollection;
-    SkSpan<const char> fText;
-    SkSpan<Block> fStyles;
-    TextStyle fDefaultStyle;
-
-    SkTArray<FontDescr> fFontSwitches;
-    FontDescr* fFontIterator;
-    SkTHashSet<FontDescr, Hash> fResolvedFonts;
-    FontDescr fFirstResolvedFont;
-
-    SkTHashMap<TextIndex, FontDescr> fFontMapping;
-    SkTArray<SkUnichar> fCodepoints;
-    SkTArray<const char*> fCharacters;
-    SkTArray<size_t> fUnresolvedIndexes;
-    SkTArray<SkUnichar> fUnresolvedCodepoints;
-    SkTHashMap<size_t, FontDescr> fWhitespaces;
-    size_t fUnresolved;
-};
-}  // namespace textlayout
-}  // namespace skia
-
-#endif  // FontResolver_DEFINED
diff --git a/modules/skparagraph/src/Iterators.h b/modules/skparagraph/src/Iterators.h
index 213fa95..8f3f95e 100644
--- a/modules/skparagraph/src/Iterators.h
+++ b/modules/skparagraph/src/Iterators.h
@@ -8,7 +8,6 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkFontMgr.h"
 #include "include/core/SkPictureRecorder.h"
-#include "modules/skparagraph/src/FontResolver.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
 #include "src/core/SkSpan.h"
 #include "src/utils/SkUTF.h"
@@ -16,41 +15,25 @@
 namespace skia {
 namespace textlayout {
 
-class FontIterator final : public SkShaper::FontRunIterator {
+class SingleFontIterator final : public SkShaper::FontRunIterator {
 public:
-    FontIterator(SkSpan<const char> utf8, FontResolver* fontResolver)
-        : fText(utf8), fCurrentChar(utf8.begin()), fFontResolver(fontResolver) { }
+    SingleFontIterator(SkSpan<const char> utf8, const SkFont& font)
+        : fText(utf8), fCurrentChar(utf8.begin()), fFont(font) { }
 
     void consume() override {
-
         SkASSERT(fCurrentChar < fText.end());
-        fFontResolver->findNext(fCurrentChar, &fFont, &fLineHeight);
-
-        // Move until we find the first character that cannot be resolved with the current font
-        while (++fCurrentChar != fText.end()) {
-            SkFont font;
-            SkScalar height;
-            if (fFontResolver->findNext(fCurrentChar, &font, &height)) {
-                if (fFont == font && fLineHeight == height) {
-                    continue;
-                }
-                break;
-            }
-        }
+        fCurrentChar = fText.end();
     }
 
     size_t endOfCurrentRun() const override { return fCurrentChar - fText.begin(); }
     bool atEnd() const override { return fCurrentChar == fText.end(); }
     const SkFont& currentFont() const override { return fFont; }
-    SkScalar currentLineHeight() const { return fLineHeight; }
 
 private:
 
     SkSpan<const char> fText;
     const char* fCurrentChar;
     SkFont fFont;
-    SkScalar fLineHeight;
-    FontResolver* fFontResolver;
 };
 
 class LangIterator final : public SkShaper::LanguageRunIterator {
diff --git a/modules/skparagraph/src/OneLineShaper.cpp b/modules/skparagraph/src/OneLineShaper.cpp
new file mode 100644
index 0000000..2974e5e
--- /dev/null
+++ b/modules/skparagraph/src/OneLineShaper.cpp
@@ -0,0 +1,556 @@
+// Copyright 2019 Google LLC.
+
+#include "modules/skparagraph/src/Iterators.h"
+#include "modules/skparagraph/src/OneLineShaper.h"
+#include <unicode/uchar.h>
+#include <algorithm>
+#include "src/utils/SkUTF.h"
+
+namespace skia {
+namespace textlayout {
+
+namespace {
+
+SkUnichar utf8_next(const char** ptr, const char* end) {
+    SkUnichar val = SkUTF::NextUTF8(ptr, end);
+    return val < 0 ? 0xFFFD : val;
+}
+
+}
+
+void OneLineShaper::commitRunBuffer(const RunInfo&) {
+
+    fCurrentRun->commit();
+
+    auto oldUnresolvedCount = fUnresolvedBlocks.size();
+
+    // Find all unresolved blocks
+    bool nothingWasUnresolved = true;
+    sortOutGlyphs([&](GlyphRange block){
+        if (block.width() == 0) {
+            return;
+        }
+        nothingWasUnresolved = false;
+        addUnresolvedWithRun(block);
+    });
+
+    // Fill all the gaps between unresolved blocks with resolved ones
+    if (nothingWasUnresolved) {
+        // No unresolved blocks added - we resolved the block with one run entirely
+        addFullyResolved();
+        return;
+    }
+
+    auto& front = fUnresolvedBlocks.front();    // The one we need to resolve
+    auto& back = fUnresolvedBlocks.back();      // The one we have from shaper
+    if (fUnresolvedBlocks.size() == oldUnresolvedCount + 1 &&
+        front.fText.width() == back.fText.width()) {
+        // The entire block remains unresolved!
+        if (front.fRun != nullptr) {
+            back.fRun = front.fRun;
+        }
+    } else {
+        fillGaps(oldUnresolvedCount);
+    }
+}
+
+#ifdef SK_DEBUG
+void OneLineShaper::printState() {
+    SkDebugf("Resolved: %d\n", fResolvedBlocks.size());
+    for (auto& resolved : fResolvedBlocks) {
+        if (resolved.fRun ==  nullptr) {
+            SkDebugf("[%d:%d) unresolved\n",
+                    resolved.fText.start, resolved.fText.end);
+            continue;
+        }
+        SkString name("???");
+        if (resolved.fRun->fFont.getTypeface() != nullptr) {
+            resolved.fRun->fFont.getTypeface()->getFamilyName(&name);
+        }
+        SkDebugf("[%d:%d) ", resolved.fGlyphs.start, resolved.fGlyphs.end);
+        SkDebugf("[%d:%d) with %s\n",
+                resolved.fText.start, resolved.fText.end,
+                name.c_str());
+    }
+
+    auto size = fUnresolvedBlocks.size();
+    SkDebugf("Unresolved: %d\n", size);
+    for (size_t i = 0; i < size; ++i) {
+        auto unresolved = fUnresolvedBlocks.front();
+        fUnresolvedBlocks.pop();
+        SkDebugf("[%d:%d)\n", unresolved.fText.start, unresolved.fText.end);
+        fUnresolvedBlocks.emplace(unresolved);
+    }
+}
+#endif
+
+void OneLineShaper::dropUnresolved() {
+    SkASSERT(!fUnresolvedBlocks.empty());
+    fUnresolvedBlocks.pop();
+}
+
+void OneLineShaper::fillGaps(size_t startingCount) {
+    // Fill out gaps between all unresolved blocks
+    TextRange resolvedTextLimits = fCurrentRun->fTextRange;
+    if (!fCurrentRun->leftToRight()) {
+        std::swap(resolvedTextLimits.start, resolvedTextLimits.end);
+    }
+    TextIndex resolvedTextStart = resolvedTextLimits.start;
+    GlyphIndex resolvedGlyphsStart = 0;
+
+    auto count = fUnresolvedBlocks.size();
+    for (size_t i = 0; i < count; ++i) {
+        auto unresolved = fUnresolvedBlocks.front();
+        fUnresolvedBlocks.pop();
+        fUnresolvedBlocks.push(unresolved);
+        if (i < startingCount) {
+            // Skip the first ones
+            continue;
+        }
+
+        TextRange resolvedText(resolvedTextStart, fCurrentRun->leftToRight() ? unresolved.fText.start : unresolved.fText.end);
+        if (resolvedText.width() > 0) {
+            if (!fCurrentRun->leftToRight()) {
+                std::swap(resolvedText.start, resolvedText.end);
+            }
+
+            GlyphRange resolvedGlyphs(resolvedGlyphsStart, unresolved.fGlyphs.start);
+            RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
+
+            fResolvedBlocks.emplace_back(resolved);
+        }
+        resolvedGlyphsStart = unresolved.fGlyphs.end;
+        resolvedTextStart =  fCurrentRun->leftToRight()
+                                ? unresolved.fText.end
+                                : unresolved.fText.start;
+    }
+
+    TextRange resolvedText(resolvedTextStart, resolvedTextLimits.end);
+    if (resolvedText.width() > 0) {
+        if (!fCurrentRun->leftToRight()) {
+            std::swap(resolvedText.start, resolvedText.end);
+        }
+
+        GlyphRange resolvedGlyphs(resolvedGlyphsStart, fCurrentRun->size());
+        RunBlock resolved(fCurrentRun, resolvedText, resolvedGlyphs, resolvedGlyphs.width());
+
+        fResolvedBlocks.emplace_back(resolved);
+    }
+}
+
+void OneLineShaper::finish(TextRange blockText, SkScalar height, SkScalar& advanceX) {
+
+    // Add all unresolved blocks to resolved blocks
+    while (!fUnresolvedBlocks.empty()) {
+        auto unresolved = fUnresolvedBlocks.front();
+        fUnresolvedBlocks.pop();
+        fResolvedBlocks.emplace_back(unresolved);
+        fUnresolvedGlyphs += unresolved.fGlyphs.width();
+    }
+
+    // Sort all pieces by text
+    std::sort(fResolvedBlocks.begin(), fResolvedBlocks.end(),
+              [](const RunBlock& a, const RunBlock& b) {
+                return a.fText.start < b.fText.start;
+              });
+
+    // Go through all of them
+    size_t lastTextEnd = blockText.start;
+    for (auto& block : fResolvedBlocks) {
+
+        if (block.fText.end <= blockText.start) {
+            continue;
+        }
+
+        if (block.fRun != nullptr) {
+            fParagraph->fFontSwitches.emplace_back(block.fText.start, block.fRun->fFont);
+        }
+
+        auto run = block.fRun;
+        auto glyphs = block.fGlyphs;
+        auto text = block.fText;
+        if (lastTextEnd != text.start) {
+            SkDEBUGF("Text ranges mismatch: ...:%d] - [%d:%d] (%d-%d)\n", lastTextEnd, text.start, text.end,  glyphs.start, glyphs.end);
+            SkASSERT(false);
+        }
+        lastTextEnd = text.end;
+
+        if (block.isFullyResolved()) {
+            // Just move the entire run
+            block.fRun->fIndex = this->fParagraph->fRuns.size();
+            this->fParagraph->fRuns.emplace_back(*block.fRun.get());
+            block.fRun.reset();
+            continue;
+        } else if (run == nullptr) {
+            continue;
+        }
+
+        auto runAdvance = SkVector::Make(run->posX(glyphs.end) - run->posX(glyphs.start), run->fAdvance.fY);
+        const SkShaper::RunHandler::RunInfo info = {
+                run->fFont, run->fBidiLevel, runAdvance, glyphs.width(),
+                SkShaper::RunHandler::Range(text.start - run->fClusterStart, text.width())};
+        this->fParagraph->fRuns.emplace_back(
+                    this->fParagraph,
+                    info,
+                    run->fClusterStart,
+                    height,
+                    this->fParagraph->fRuns.count(),
+                    advanceX
+                );
+        auto piece = &this->fParagraph->fRuns.back();
+
+        // TODO: Optimize copying
+        for (size_t i = glyphs.start; i <= glyphs.end; ++i) {
+
+            auto index = i - glyphs.start;
+            if (i < glyphs.end) {
+                piece->fGlyphs[index] = run->fGlyphs[i];
+                piece->fBounds[index] = run->fBounds[i];
+            }
+            piece->fClusterIndexes[index] = run->fClusterIndexes[i];
+            piece->fOffsets[index] = run->fOffsets[i];
+            piece->fPositions[index] = run->fPositions[i];
+            piece->addX(index, advanceX);
+        }
+
+        // Carve out the line text out of the entire run text
+        fAdvance.fX += runAdvance.fX;
+        fAdvance.fY = SkMaxScalar(fAdvance.fY, runAdvance.fY);
+    }
+
+    advanceX = fAdvance.fX;
+    if (lastTextEnd != blockText.end) {
+        SkDEBUGF("Last range mismatch: %d - %d\n", lastTextEnd, blockText.end);
+        SkASSERT(false);
+    }
+}
+
+void OneLineShaper::increment(TextIndex& index) {
+    auto text = fCurrentRun->fMaster->text();
+    auto cluster = text.begin() + index;
+
+    if (cluster < text.end()) {
+        utf8_next(&cluster, text.end());
+        index = cluster - text.begin();
+    }
+}
+
+// Make it [left:right) regardless of a text direction
+TextRange OneLineShaper::normalizeTextRange(GlyphRange glyphRange) {
+    TextRange textRange(clusterIndex(glyphRange.start), clusterIndex(glyphRange.end));
+    if (!fCurrentRun->leftToRight()) {
+        std::swap(textRange.start, textRange.end);
+    }
+    return textRange;
+}
+
+void OneLineShaper::addFullyResolved() {
+    if (this->fCurrentRun->size() == 0) {
+        return;
+    }
+    RunBlock resolved(fCurrentRun,
+                      this->fCurrentRun->fTextRange,
+                      GlyphRange(0, this->fCurrentRun->size()),
+                      this->fCurrentRun->size());
+    fResolvedBlocks.emplace_back(resolved);
+}
+
+void OneLineShaper::addUnresolvedWithRun(GlyphRange glyphRange) {
+    RunBlock unresolved(fCurrentRun, clusteredText(glyphRange), glyphRange, 0);
+    if (unresolved.fGlyphs.width() == fCurrentRun->size()) {
+        SkASSERT(unresolved.fText.width() == fCurrentRun->fTextRange.width());
+    } else if (!fUnresolvedBlocks.empty()) {
+        auto& lastUnresolved = fUnresolvedBlocks.back();
+        if (lastUnresolved.fRun != nullptr &&
+            lastUnresolved.fRun->fIndex == fCurrentRun->fIndex) {
+
+            if (lastUnresolved.fText.end == unresolved.fText.start) {
+                // Two pieces next to each other - can join them
+                lastUnresolved.fText.end = unresolved.fText.end;
+                lastUnresolved.fGlyphs.end = glyphRange.end;
+                return;
+            } else if (lastUnresolved.fText.intersects(unresolved.fText)) {
+                // Few pieces of the same unresolved text block can ignore the second one
+                lastUnresolved.fGlyphs.start =
+                        SkTMin(lastUnresolved.fGlyphs.start, glyphRange.start);
+                lastUnresolved.fGlyphs.end = SkTMax(lastUnresolved.fGlyphs.end, glyphRange.end);
+                lastUnresolved.fText = clusteredText(lastUnresolved.fGlyphs);
+                return;
+            }
+        }
+    }
+    fUnresolvedBlocks.emplace(unresolved);
+}
+
+void OneLineShaper::sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock) {
+
+    auto text = fCurrentRun->fMaster->text();
+    size_t unresolvedGlyphs = 0;
+
+    GlyphRange block = EMPTY_RANGE;
+    for (size_t i = 0; i < fCurrentRun->size(); ++i) {
+
+        // Inspect the glyph
+        auto glyph = fCurrentRun->fGlyphs[i];
+        if (glyph != 0) {
+            if (block.start == EMPTY_INDEX) {
+                // Keep skipping resolved code points
+                continue;
+            }
+            // This is the end of unresolved block
+            block.end = i;
+        } else {
+            const char* cluster = text.begin() + clusterIndex(i);
+            SkUnichar codepoint = utf8_next(&cluster, text.end());
+            if (u_iscntrl(codepoint)) {
+                // This codepoint does not have to be resolved; let's pretend it's resolved
+                if (block.start == EMPTY_INDEX) {
+                    // Keep skipping resolved code points
+                    continue;
+                }
+                // This is the end of unresolved block
+                block.end = i;
+            } else {
+                ++unresolvedGlyphs;
+                if (block.start == EMPTY_INDEX) {
+                    // Start new unresolved block
+                    block.start = i;
+                    block.end = EMPTY_INDEX;
+                } else {
+                    // Keep skipping unresolved block
+                }
+                continue;
+            }
+        }
+
+        // Found an unresolved block
+        sortOutUnresolvedBLock(block);
+        block = EMPTY_RANGE;
+    }
+
+    // One last block could have been left
+    if (block.start != EMPTY_INDEX) {
+        block.end = fCurrentRun->size();
+        sortOutUnresolvedBLock(block);
+    }
+
+}
+
+void OneLineShaper::iterateThroughFontStyles(SkSpan<Block> styleSpan,
+                                             const ShapeSingleFontVisitor& visitor) {
+    Block combinedBlock;
+    for (auto& block : styleSpan) {
+        SkASSERT(combinedBlock.fRange.width() == 0 ||
+                 combinedBlock.fRange.end == block.fRange.start);
+
+        if (!combinedBlock.fRange.empty()) {
+            if (block.fStyle.matchOneAttribute(StyleType::kFont, combinedBlock.fStyle)) {
+                combinedBlock.add(block.fRange);
+                continue;
+            }
+            // Resolve all characters in the block for this style
+            visitor(combinedBlock);
+        }
+
+        combinedBlock.fRange = block.fRange;
+        combinedBlock.fStyle = block.fStyle;
+    }
+
+    visitor(combinedBlock);
+#ifdef SK_DEBUG
+    //printState();
+#endif
+}
+
+void OneLineShaper::matchResolvedFonts(const TextStyle& textStyle,
+                                       const TypefaceVisitor& visitor) {
+    std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(textStyle.getFontFamilies(), textStyle.getFontStyle());
+
+    for (const auto& typeface : typefaces) {
+        if (!visitor(typeface))
+            return;
+    }
+
+    if (fParagraph->fFontCollection->fontFallbackEnabled()) {
+        // Give fallback a clue
+        SkASSERT(!fUnresolvedBlocks.empty());
+        auto unresolvedRange = fUnresolvedBlocks.front().fText;
+        auto unresolvedText = fParagraph->text(unresolvedRange);
+        const char* ch = unresolvedText.begin();
+        SkUnichar unicode = utf8_next(&ch, unresolvedText.end());
+
+        auto typeface = fParagraph->fFontCollection->defaultFallback(
+                unicode, textStyle.getFontStyle(), textStyle.getLocale());
+
+        if (typeface != nullptr) {
+            if (!visitor(typeface)) {
+                return;
+            }
+        }
+    }
+}
+
+bool OneLineShaper::iterateThroughShapingRegions(const ShapeVisitor& shape) {
+
+    SkScalar advanceX = 0;
+    for (auto& placeholder : fParagraph->fPlaceholders) {
+        // Shape the text
+        if (placeholder.fTextBefore.width() > 0) {
+            // Set up the iterators
+            SkSpan<const char> textSpan = fParagraph->text(placeholder.fTextBefore);
+            SkSpan<Block> styleSpan(fParagraph->fTextStyles.begin() + placeholder.fBlocksBefore.start,
+                                    placeholder.fBlocksBefore.width());
+
+            if (!shape(textSpan, styleSpan, advanceX, placeholder.fTextBefore.start)) {
+                return false;
+            }
+        }
+
+        if (placeholder.fRange.width() == 0) {
+            continue;
+        }
+
+        // Get the placeholder font
+        std::vector<sk_sp<SkTypeface>> typefaces = fParagraph->fFontCollection->findTypefaces(
+            placeholder.fTextStyle.getFontFamilies(),
+            placeholder.fTextStyle.getFontStyle());
+        sk_sp<SkTypeface> typeface = typefaces.size() ? typefaces.front() : nullptr;
+        SkFont font(typeface, placeholder.fTextStyle.getFontSize());
+
+        // "Shape" the placeholder
+        const SkShaper::RunHandler::RunInfo runInfo = {
+            font,
+            (uint8_t)2,
+            SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
+            1,
+            SkShaper::RunHandler::Range(placeholder.fRange.start, placeholder.fRange.width())
+        };
+        auto& run = fParagraph->fRuns.emplace_back(this->fParagraph,
+                                       runInfo,
+                                       0,
+                                       1.0f,
+                                       fParagraph->fRuns.count(),
+                                       advanceX);
+
+        run.fPositions[0] = { advanceX, 0 };
+        run.fOffsets[0] = {0, 0};
+        run.fClusterIndexes[0] = 0;
+        run.fPlaceholder = &placeholder.fStyle;
+        advanceX += placeholder.fStyle.fWidth;
+    }
+    return true;
+}
+
+bool OneLineShaper::shape() {
+
+    // The text can be broken into many shaping sequences
+    // (by place holders, possibly, by hard line breaks or tabs, too)
+    uint8_t textDirection = fParagraph->fParagraphStyle.getTextDirection() == TextDirection::kLtr  ? 2 : 1;
+    auto limitlessWidth = std::numeric_limits<SkScalar>::max();
+
+    auto result = iterateThroughShapingRegions(
+            [this, textDirection, limitlessWidth]
+            (SkSpan<const char> textSpan, SkSpan<Block> styleSpan, SkScalar& advanceX, TextIndex textStart) {
+
+        // Set up the shaper and shape the next
+        auto shaper = SkShaper::MakeShapeDontWrapOrReorder();
+        if (shaper == nullptr) {
+            // For instance, loadICU does not work. We have to stop the process
+            return false;
+        }
+
+        iterateThroughFontStyles(styleSpan,
+                [this, &shaper, textDirection, limitlessWidth, &advanceX](Block block) {
+            auto blockSpan = SkSpan<Block>(&block, 1);
+
+            // Start from the beginning (hoping that it's a simple case one block - one run)
+            fHeight = block.fStyle.getHeight();
+            fAdvance = SkVector::Make(advanceX, 0);
+            fCurrentText = block.fRange;
+            fUnresolvedBlocks.emplace(RunBlock(block.fRange));
+
+            matchResolvedFonts(block.fStyle, [&](sk_sp<SkTypeface> typeface) {
+                // Create one more font to try
+                SkFont font(std::move(typeface), block.fStyle.getFontSize());
+                font.setEdging(SkFont::Edging::kAntiAlias);
+                font.setHinting(SkFontHinting::kSlight);
+                font.setSubpixel(true);
+
+                // Walk through all the currently unresolved blocks
+                // (ignoring those that appear later)
+                auto count = fUnresolvedBlocks.size();
+                while (count-- > 0) {
+                    auto unresolvedRange = fUnresolvedBlocks.front().fText;
+                    auto unresolvedText = fParagraph->text(unresolvedRange);
+
+                    SingleFontIterator fontIter(unresolvedText, font);
+                    LangIterator lang(unresolvedText, blockSpan,
+                                      fParagraph->paragraphStyle().getTextStyle());
+                    auto script = SkShaper::MakeHbIcuScriptRunIterator(unresolvedText.begin(),
+                                                                       unresolvedText.size());
+                    auto bidi = SkShaper::MakeIcuBiDiRunIterator(
+                            unresolvedText.begin(), unresolvedText.size(), textDirection);
+                    if (bidi == nullptr) {
+                        return false;
+                    }
+
+                    fCurrentText = unresolvedRange;
+                    shaper->shape(unresolvedText.begin(), unresolvedText.size(), fontIter, *bidi,
+                                  *script, lang, limitlessWidth, this);
+
+                    // Take off the queue the block we tried to resolved -
+                    // whatever happened, we have now smaller pieces of it to deal with
+                    this->dropUnresolved();
+                }
+
+                // Continue until we resolved all the code points
+                return !fUnresolvedBlocks.empty();
+            });
+
+            this->finish(block.fRange, block.fStyle.getHeight(), advanceX);
+        });
+
+        return true;
+    });
+
+    return result;
+}
+
+TextRange OneLineShaper::clusteredText(GlyphRange glyphs) {
+
+    enum class Dir { left, right };
+    enum class Pos { inclusive, exclusive };
+
+    // [left: right)
+    auto findBaseChar = [&](TextIndex index, Dir dir) -> TextIndex {
+
+        if (!fCurrentRun->leftToRight()) {
+            ++index;
+        }
+        if (dir == Dir::right) {
+            while (index < fCurrentRun->fTextRange.end) {
+                if (this->fParagraph->fGraphemes.contains(index)) {
+                    return index;
+                }
+                ++index;
+            }
+            return fCurrentRun->fTextRange.end;
+        } else {
+            while (index >= fCurrentRun->fTextRange.start) {
+                if (this->fParagraph->fGraphemes.contains(index)) {
+                    return index;
+                }
+                --index;
+            }
+            return fCurrentRun->fTextRange.start;
+        }
+    };
+
+    TextRange textRange(normalizeTextRange(glyphs));
+    textRange.start = findBaseChar(textRange.start, Dir::left);
+    textRange.end = findBaseChar(textRange.end, Dir::right);
+
+    return { textRange.start, textRange.end };
+}
+}
+}
diff --git a/modules/skparagraph/src/OneLineShaper.h b/modules/skparagraph/src/OneLineShaper.h
new file mode 100644
index 0000000..e25c085
--- /dev/null
+++ b/modules/skparagraph/src/OneLineShaper.h
@@ -0,0 +1,113 @@
+// Copyright 2019 Google LLC.
+#ifndef LineBreaker_DEFINED
+#define LineBreaker_DEFINED
+
+#include <functional>  // std::function
+#include <queue>
+#include "modules/skparagraph/include/TextStyle.h"
+#include "modules/skparagraph/src/ParagraphImpl.h"
+#include "modules/skparagraph/src/Run.h"
+#include "src/core/SkSpan.h"
+
+namespace skia {
+namespace textlayout {
+
+typedef size_t GlyphIndex;
+typedef SkRange<GlyphIndex> GlyphRange;
+
+class ParagraphImpl;
+class OneLineShaper : public SkShaper::RunHandler {
+public:
+    explicit OneLineShaper(ParagraphImpl* paragraph)
+        : fParagraph(paragraph)
+        , fHeight(0.0f)
+        , fAdvance(SkPoint::Make(0.0f, 0.0f))
+        , fUnresolvedGlyphs(0) { }
+
+    bool shape();
+
+    size_t unresolvedGlyphs() { return fUnresolvedGlyphs; }
+
+private:
+
+    struct RunBlock {
+        RunBlock() : fRun(nullptr) { }
+
+        // First unresolved block
+        explicit RunBlock(TextRange text) : fRun(nullptr), fText(text) { }
+
+        RunBlock(std::shared_ptr<Run> run, TextRange text, GlyphRange glyphs, size_t score)
+            : fRun(std::move(run))
+            , fText(text)
+            , fGlyphs(glyphs) { }
+
+        // Entire run comes as one block fully resolved
+        explicit RunBlock(std::shared_ptr<Run> run)
+            : fRun(std::move(run))
+            , fText(run->fTextRange)
+            , fGlyphs(GlyphRange(0, run->size())) { }
+
+        std::shared_ptr<Run> fRun;
+        TextRange fText;
+        GlyphRange fGlyphs;
+        bool isFullyResolved() { return fRun != nullptr && fGlyphs.width() == fRun->size(); }
+    };
+
+    using ShapeVisitor =
+            std::function<SkScalar(SkSpan<const char>, SkSpan<Block>, SkScalar&, TextIndex)>;
+    bool iterateThroughShapingRegions(const ShapeVisitor& shape);
+
+    using ShapeSingleFontVisitor = std::function<void(Block)>;
+    void iterateThroughFontStyles(SkSpan<Block> styleSpan, const ShapeSingleFontVisitor& visitor);
+
+    using TypefaceVisitor = std::function<bool(sk_sp<SkTypeface> typeface)>;
+    void matchResolvedFonts(const TextStyle& textStyle, const TypefaceVisitor& visitor);
+#ifdef SK_DEBUG
+    void printState();
+#endif
+    void dropUnresolved();
+    void finish(TextRange text, SkScalar height, SkScalar& advanceX);
+
+    void beginLine() override {}
+    void runInfo(const RunInfo&) override {}
+    void commitRunInfo() override {}
+    void commitLine() override {}
+
+    Buffer runBuffer(const RunInfo& info) override {
+        fCurrentRun = std::make_shared<Run>(fParagraph,
+                                           info,
+                                           fCurrentText.start,
+                                           fHeight,
+                                           fParagraph->fRuns.count(),
+                                           fAdvance.fX);
+        return fCurrentRun->newRunBuffer();
+    }
+
+    void commitRunBuffer(const RunInfo&) override;
+
+    TextRange clusteredText(GlyphRange glyphs);
+    ClusterIndex clusterIndex(GlyphIndex glyph) {
+        return fCurrentText.start + fCurrentRun->fClusterIndexes[glyph];
+    }
+    void addFullyResolved();
+    void addUnresolvedWithRun(GlyphRange glyphRange);
+    void sortOutGlyphs(std::function<void(GlyphRange)>&& sortOutUnresolvedBLock);
+    ClusterRange normalizeTextRange(GlyphRange glyphRange);
+    void increment(TextIndex& index);
+    void fillGaps(size_t);
+
+    ParagraphImpl* fParagraph;
+    TextRange fCurrentText;
+    SkScalar fHeight;
+    SkVector fAdvance;
+    size_t fUnresolvedGlyphs;
+
+    // TODO: Something that is not thead-safe since we don't need it
+    std::shared_ptr<Run> fCurrentRun;
+    std::queue<RunBlock> fUnresolvedBlocks;
+    std::vector<RunBlock> fResolvedBlocks;
+};
+
+}
+}
+#endif
diff --git a/modules/skparagraph/src/ParagraphCache.cpp b/modules/skparagraph/src/ParagraphCache.cpp
index 80da392..05d951c 100644
--- a/modules/skparagraph/src/ParagraphCache.cpp
+++ b/modules/skparagraph/src/ParagraphCache.cpp
@@ -9,12 +9,12 @@
 public:
     ParagraphCacheKey(const ParagraphImpl* paragraph)
         : fText(paragraph->fText.c_str(), paragraph->fText.size())
-        , fFontSwitches(paragraph->switches())
+        , fResolvedFonts(paragraph->resolvedFonts())
         , fTextStyles(paragraph->fTextStyles)
         , fParagraphStyle(paragraph->paragraphStyle()) { }
 
     SkString fText;
-    SkTArray<FontDescr> fFontSwitches;
+    SkTArray<ResolvedFontDescriptor> fResolvedFonts;
     SkTArray<Block, true> fTextStyles;
     ParagraphStyle fParagraphStyle;
 };
@@ -23,18 +23,19 @@
 public:
     ParagraphCacheValue(const ParagraphImpl* paragraph)
         : fKey(ParagraphCacheKey(paragraph))
-        , fInternalState(paragraph->state())
+        , fInternalState(paragraph->fState)
         , fRuns(paragraph->fRuns)
-        , fClusters(paragraph->fClusters) { }
+        , fClusters(paragraph->fClusters)
+        , fUnresolvedGlyphs(paragraph->fUnresolvedGlyphs){ }
 
     // Input == key
     ParagraphCacheKey fKey;
 
     // Shaped results:
     InternalState fInternalState;
-    SkTArray<Run> fRuns;
+    SkTArray<Run, false> fRuns;
     SkTArray<Cluster, true> fClusters;
-    SkTArray<RunShifts, true> fRunShifts;
+    size_t fUnresolvedGlyphs;
 };
 
 
@@ -46,8 +47,8 @@
 }
 uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const {
     uint32_t hash = 0;
-    for (auto& fd : key.fFontSwitches) {
-        hash = mix(hash, SkGoodHash()(fd.fStart));
+    for (auto& fd : key.fResolvedFonts) {
+        hash = mix(hash, SkGoodHash()(fd.fTextStart));
         hash = mix(hash, SkGoodHash()(fd.fFont.getSize()));
 
         if (fd.fFont.getTypeface() != nullptr) {
@@ -74,7 +75,7 @@
     if (a.fText.size() != b.fText.size()) {
         return false;
     }
-    if (a.fFontSwitches.count() != b.fFontSwitches.count()) {
+    if (a.fResolvedFonts.count() != b.fResolvedFonts.count()) {
         return false;
     }
     if (a.fText != b.fText) {
@@ -89,10 +90,10 @@
         return false;
     }
 
-    for (size_t i = 0; i < a.fFontSwitches.size(); ++i) {
-        auto& fda = a.fFontSwitches[i];
-        auto& fdb = b.fFontSwitches[i];
-        if (fda.fStart != fdb.fStart) {
+    for (size_t i = 0; i < a.fResolvedFonts.size(); ++i) {
+        auto& fda = a.fResolvedFonts[i];
+        auto& fdb = b.fResolvedFonts[i];
+        if (fda.fTextStart != fdb.fTextStart) {
             return false;
         }
         if (fda.fFont != fdb.fFont) {
@@ -149,7 +150,6 @@
 void ParagraphCache::updateFrom(const ParagraphImpl* paragraph, Entry* entry) {
 
     entry->fValue->fInternalState = paragraph->state();
-    entry->fValue->fRunShifts = paragraph->fRunShifts;
     for (size_t i = 0; i < paragraph->fRuns.size(); ++i) {
         auto& run = paragraph->fRuns[i];
         if (run.fSpaced) {
@@ -171,12 +171,8 @@
         cluster.setMaster(paragraph);
     }
 
-    paragraph->fRunShifts.reset();
-    for (auto& runShift : entry->fValue->fRunShifts) {
-        paragraph->fRunShifts.push_back(runShift);
-    }
-
     paragraph->fState = entry->fValue->fInternalState;
+    paragraph->fUnresolvedGlyphs = entry->fValue->fUnresolvedGlyphs;
 }
 
 void ParagraphCache::printStatistics() {
diff --git a/modules/skparagraph/src/ParagraphImpl.cpp b/modules/skparagraph/src/ParagraphImpl.cpp
index 43c4a68..26d128e 100644
--- a/modules/skparagraph/src/ParagraphImpl.cpp
+++ b/modules/skparagraph/src/ParagraphImpl.cpp
@@ -3,7 +3,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkFontMgr.h"
 #include "include/core/SkPictureRecorder.h"
-#include "modules/skparagraph/src/Iterators.h"
+#include "modules/skparagraph/src/OneLineShaper.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
 #include "modules/skparagraph/src/Run.h"
 #include "modules/skparagraph/src/TextWrapper.h"
@@ -11,6 +11,7 @@
 #include "src/utils/SkUTF.h"
 #include <algorithm>
 #include <unicode/ustring.h>
+#include <queue>
 
 namespace skia {
 namespace textlayout {
@@ -23,6 +24,7 @@
     // This rounding is done to match Flutter tests. Must be removed..
   return SkScalarRoundToScalar(a * 100.0)/100.0;
 }
+
 }
 
 TextRange operator*(const TextRange& a, const TextRange& b) {
@@ -71,10 +73,12 @@
         , fPlaceholders(std::move(placeholders))
         , fText(text)
         , fState(kUnknown)
+        , fUnresolvedGlyphs(0)
         , fPicture(nullptr)
         , fStrutMetrics(false)
         , fOldWidth(0)
-        , fOldHeight(0) {
+        , fOldHeight(0)
+        , fOrigin(SkRect::MakeEmpty()) {
     // TODO: extractStyles();
 }
 
@@ -87,10 +91,12 @@
         , fTextStyles(std::move(blocks))
         , fPlaceholders(std::move(placeholders))
         , fState(kUnknown)
+        , fUnresolvedGlyphs(0)
         , fPicture(nullptr)
         , fStrutMetrics(false)
         , fOldWidth(0)
-        , fOldHeight(0) {
+        , fOldHeight(0)
+        , fOrigin(SkRect::MakeEmpty()) {
     icu::UnicodeString unicode((UChar*)utf16text.data(), SkToS32(utf16text.size()));
     std::string str;
     unicode.toUTF8String(str);
@@ -100,6 +106,14 @@
 
 ParagraphImpl::~ParagraphImpl() = default;
 
+int32_t ParagraphImpl::unresolvedGlyphs() {
+    if (fState < kShaped) {
+        return -1;
+    }
+
+    return fUnresolvedGlyphs;
+}
+
 void ParagraphImpl::layout(SkScalar rawWidth) {
 
     // TODO: This rounding is done to match Flutter tests. Must be removed...
@@ -107,6 +121,7 @@
     if (fState < kShaped) {
         // Layout marked as dirty for performance/testing reasons
         this->fRuns.reset();
+        this->fRunShifts.reset();
         this->fClusters.reset();
     } else if (fState >= kLineBroken && (fOldWidth != floorWidth || fOldHeight != fHeight)) {
         // We can use the results from SkShaper but have to break lines again
@@ -115,6 +130,8 @@
 
     if (fState < kShaped) {
         fClusters.reset();
+        fGraphemes.reset();
+        this->markGraphemes();
 
         if (!this->shapeTextIntoEndlessLine()) {
 
@@ -208,7 +225,7 @@
         fState = kDrawn;
     }
 
-    SkMatrix matrix = SkMatrix::MakeTrans(x, y);
+    SkMatrix matrix = SkMatrix::MakeTrans(x + fOrigin.fLeft, y + fOrigin.fTop);
     canvas->drawPicture(fPicture, &matrix, nullptr);
 }
 
@@ -237,11 +254,11 @@
             if (!fClusters.empty()) {
                 fClusters.back().setBreakType(Cluster::SoftLineBreak);
             }
-            auto& cluster = fClusters.emplace_back(this, runIndex, 0ul, 0ul, text,
-                                                   run.advance().fX, run.advance().fY);
+            auto& cluster = fClusters.emplace_back(this, runIndex, 0ul, 0ul, text, run.advance().fX,
+                                                   run.advance().fY);
             cluster.setBreakType(Cluster::SoftLineBreak);
         } else {
-            fClusters.reserve(fClusters.size() + fRuns.size());
+            fClusters.reserve(fClusters.size() + run.size());
             // Walk through the glyph in the direction of input text
             run.iterateThroughClustersInTextOrder([runIndex, this](size_t glyphStart,
                                                                    size_t glyphEnd,
@@ -342,156 +359,28 @@
 
 bool ParagraphImpl::shapeTextIntoEndlessLine() {
 
-    class ShapeHandler final : public SkShaper::RunHandler {
-    public:
-        explicit ShapeHandler(ParagraphImpl& paragraph, size_t firstChar, FontIterator* fontIterator, SkScalar advanceX)
-                : fParagraph(&paragraph)
-                , fFirstChar(firstChar)
-                , fFontIterator(fontIterator)
-                , fAdvance(SkVector::Make(advanceX, 0)) {}
-
-        SkVector advance() const { return fAdvance; }
-
-    private:
-        void beginLine() override {}
-
-        void runInfo(const RunInfo&) override {}
-
-        void commitRunInfo() override {}
-
-        Buffer runBuffer(const RunInfo& info) override {
-            auto& run = fParagraph->fRuns.emplace_back(fParagraph,
-                                                       info,
-                                                       fFirstChar,
-                                                       fFontIterator->currentLineHeight(),
-                                                       fParagraph->fRuns.count(),
-                                                       fAdvance.fX);
-            return run.newRunBuffer();
-        }
-
-        void commitRunBuffer(const RunInfo&) override {
-
-            auto& run = fParagraph->fRuns.back();
-            if (run.size() == 0) {
-                fParagraph->fRuns.pop_back();
-                return;
-            }
-
-            // Carve out the line text out of the entire run text
-            fAdvance.fX += run.advance().fX;
-            fAdvance.fY = SkMaxScalar(fAdvance.fY, run.advance().fY);
-            run.fPlaceholder = nullptr;
-        }
-
-        void commitLine() override {}
-
-        ParagraphImpl* fParagraph;
-        size_t fFirstChar;
-        FontIterator* fFontIterator;
-        SkVector fAdvance;
-    };
-
-    // This is a pretty big step - resolving all characters against all given fonts
-    fFontResolver.findAllFontsForAllStyledBlocks(this);
-
-    if (fText.size() == 0 || fFontResolver.switches().size() == 0) {
+    if (fText.size() == 0) {
         return false;
     }
 
     // Check the font-resolved text against the cache
-    if (!fFontCollection->getParagraphCache()->findParagraph(this)) {
-        // The text can be broken into many shaping sequences
-        // (by place holders, possibly, by hard line breaks or tabs, too)
-        uint8_t textDirection = fParagraphStyle.getTextDirection() == TextDirection::kLtr  ? 2 : 1;
-        auto limitlessWidth = std::numeric_limits<SkScalar>::max();
-
-        auto result = iterateThroughShapingRegions(
-                [this, textDirection, limitlessWidth]
-                (SkSpan<const char> textSpan, SkSpan<Block> styleSpan, SkScalar& advanceX, size_t start) {
-
-            LangIterator lang(textSpan, styleSpan, paragraphStyle().getTextStyle());
-            FontIterator font(textSpan, &fFontResolver);
-            auto script = SkShaper::MakeHbIcuScriptRunIterator(textSpan.begin(), textSpan.size());
-            auto bidi = SkShaper::MakeIcuBiDiRunIterator(textSpan.begin(), textSpan.size(), textDirection);
-            if (bidi == nullptr) {
-                return false;
-            }
-
-            // Set up the shaper and shape the next
-            ShapeHandler handler(*this, start, &font, advanceX);
-            auto shaper = SkShaper::MakeShapeDontWrapOrReorder();
-            SkASSERT_RELEASE(shaper != nullptr);
-            shaper->shape(textSpan.begin(), textSpan.size(), font, *bidi, *script, lang, limitlessWidth, &handler);
-            advanceX =  handler.advance().fX;
-            return true;
-        });
-        if (!result) {
-            return false;
-        }
+    if (fFontCollection->getParagraphCache()->findParagraph(this)) {
+        this->fRunShifts.reset();
+        return true;
     }
 
-    // Clean the array for justification
-    if (fParagraphStyle.getTextAlign() == TextAlign::kJustify) {
-        fRunShifts.reset();
-        fRunShifts.push_back_n(fRuns.size(), RunShifts());
-        for (size_t i = 0; i < fRuns.size(); ++i) {
-            fRunShifts[i].fShifts.push_back_n(fRuns[i].size() + 1, 0.0);
-        }
+    fFontSwitches.reset();
+
+    OneLineShaper oneLineShaper(this);
+    auto result = oneLineShaper.shape();
+    fUnresolvedGlyphs = oneLineShaper.unresolvedGlyphs();
+
+    if (!result) {
+        return false;
+    } else {
+        this->fRunShifts.reset();
+        return true;
     }
-
-    return true;
-}
-
-bool ParagraphImpl::iterateThroughShapingRegions(ShapeVisitor shape) {
-
-    SkScalar advanceX = 0;
-    for (auto& placeholder : fPlaceholders) {
-        // Shape the text
-        if (placeholder.fTextBefore.width() > 0) {
-            // Set up the iterators
-            SkSpan<const char> textSpan = this->text(placeholder.fTextBefore);
-            SkSpan<Block> styleSpan(fTextStyles.begin() + placeholder.fBlocksBefore.start,
-                                    placeholder.fBlocksBefore.width());
-
-            if (!shape(textSpan, styleSpan, advanceX, placeholder.fTextBefore.start)) {
-                return false;
-            }
-        }
-
-        if (placeholder.fRange.width() == 0) {
-            continue;
-        }
-
-        // Get the placeholder font
-        sk_sp<SkTypeface> typeface = nullptr;
-        for (auto& ff : placeholder.fTextStyle.getFontFamilies()) {
-            typeface = fFontCollection->matchTypeface(ff.c_str(), placeholder.fTextStyle.getFontStyle(), placeholder.fTextStyle.getLocale());
-            if (typeface != nullptr) {
-                break;
-            }
-        }
-        SkFont font(typeface, placeholder.fTextStyle.getFontSize());
-
-        // "Shape" the placeholder
-        const SkShaper::RunHandler::RunInfo runInfo = {
-            font,
-            (uint8_t)2,
-            SkPoint::Make(placeholder.fStyle.fWidth, placeholder.fStyle.fHeight),
-            1,
-            SkShaper::RunHandler::Range(placeholder.fRange.start, placeholder.fRange.width())
-        };
-        auto& run = fRuns.emplace_back(this,
-                                       runInfo,
-                                       0,
-                                       1,
-                                       fRuns.count(),
-                                       advanceX);
-        run.fPositions[0] = { advanceX, 0 };
-        run.fClusterIndexes[0] = 0;
-        run.fPlaceholder = &placeholder.fStyle;
-        advanceX += placeholder.fStyle.fWidth;
-    }
-    return true;
 }
 
 void ParagraphImpl::breakShapedTextIntoLines(SkScalar maxWidth) {
@@ -531,6 +420,9 @@
 
 void ParagraphImpl::formatLines(SkScalar maxWidth) {
     auto effectiveAlign = fParagraphStyle.effective_align();
+    if (effectiveAlign == TextAlign::kJustify) {
+        this->resetRunShifts();
+    }
     for (auto& line : fLines) {
         if (&line == &fLines.back() && effectiveAlign == TextAlign::kJustify) {
             effectiveAlign = line.assumedTextAlign();
@@ -541,7 +433,8 @@
 
 void ParagraphImpl::paintLinesIntoPicture() {
     SkPictureRecorder recorder;
-    SkCanvas* textCanvas = recorder.beginRecording(fWidth, fHeight, nullptr, 0);
+    SkCanvas* textCanvas = recorder.beginRecording(fOrigin.width(), fOrigin.height(), nullptr, 0);
+    textCanvas->translate(-fOrigin.fLeft, -fOrigin.fTop);
 
     for (auto& line : fLines) {
         line.paint(textCanvas);
@@ -556,24 +449,13 @@
         return;
     }
 
-    sk_sp<SkTypeface> typeface;
-    if (strutStyle.getFontFamilies().empty()) {
-        typeface = fFontCollection->matchTypeface("", strutStyle.getFontStyle(), SkString(""));
-    } else {
-        for (auto& fontFamily : strutStyle.getFontFamilies()) {
-            typeface = fFontCollection->matchTypeface(fontFamily.c_str(), strutStyle.getFontStyle(), SkString(""));
-            if (typeface.get() != nullptr) {
-                break;
-            }
-        }
-    }
-
-    if (typeface.get() == nullptr) {
+    std::vector<sk_sp<SkTypeface>> typefaces = fFontCollection->findTypefaces(strutStyle.getFontFamilies(), strutStyle.getFontStyle());
+    if (typefaces.empty()) {
         SkDEBUGF("Could not resolve strut font\n");
         return;
     }
 
-    SkFont font(typeface, strutStyle.getFontSize());
+    SkFont font(typefaces.front(), strutStyle.getFontSize());
     SkFontMetrics metrics;
     font.getMetrics(&metrics);
 
@@ -614,6 +496,36 @@
     return { begin, end + 1 };
 }
 
+void ParagraphImpl::calculateBoundaries(ClusterRange clusters, SkVector offset, SkVector advance) {
+
+    auto boundaries = SkRect::MakeXYWH(0, 0, advance.fX, advance.fY);
+
+    if (!fRuns.empty()) {
+        // TODO: Move it down to the TextWrapper to avoid extra calculations
+        auto run = &fRuns[0];
+        auto runShift = 0.0f;
+        auto clusterShift = 0.0f;
+        for (auto index = clusters.start; index < clusters.end; ++index) {
+            auto& cluster = fClusters[index];
+            if (cluster.runIndex() != run->index()) {
+                run = &fRuns[cluster.runIndex()];
+                runShift += clusterShift;
+                clusterShift = 0;
+            }
+            clusterShift += cluster.width();
+            for (auto i = cluster.startPos(); i < cluster.endPos(); ++i) {
+                auto posX = run->posX(i);
+                auto posY = run->posY(i);
+                auto bounds = run->getBounds(i);
+                bounds.offset(posX + runShift, posY);
+                boundaries.joinPossiblyEmptyRect(bounds);
+            }
+        }
+    }
+    boundaries.offset(offset);
+    fOrigin.joinPossiblyEmptyRect(boundaries);
+}
+
 TextLine& ParagraphImpl::addLine(SkVector offset,
                                  SkVector advance,
                                  TextRange text,
@@ -625,12 +537,16 @@
     // Define a list of styles that covers the line
     auto blocks = findAllBlocks(text);
 
+    auto correctedOffset = offset;
+    correctedOffset.offset(0, sizes.baseline());
+    calculateBoundaries(clusters, correctedOffset, advance);
+
     return fLines.emplace_back(this, offset, advance, blocks, text, textWithSpaces, clusters, clustersWithGhosts, widthWithSpaces, sizes);
 }
 
-void ParagraphImpl::markGraphemes() {
+void ParagraphImpl::markGraphemes16() {
 
-    if (!fGraphemes.empty()) {
+    if (!fGraphemes16.empty()) {
         return;
     }
 
@@ -648,9 +564,9 @@
         SkUnichar u = SkUTF::NextUTF8(&ptr, end);
         uint16_t buffer[2];
         size_t count = SkUTF::ToUTF16(u, buffer);
-        fCodePoints.emplace_back(EMPTY_INDEX, index);
+        fCodePoints.emplace_back(EMPTY_INDEX, index, count > 1 ? 2 : 1);
         if (count > 1) {
-            fCodePoints.emplace_back(EMPTY_INDEX, index);
+            fCodePoints.emplace_back(EMPTY_INDEX, index, 1);
         }
     }
 
@@ -666,16 +582,38 @@
             ++codepoints.end;
         }
 
-        // Update all the codepoints that belong to this grapheme
-        for (auto i = codepoints.start; i < codepoints.end; ++i) {
-            fCodePoints[i].fGrapeme = fGraphemes.size();
+        if (startPos == endPos) {
+            continue;
         }
 
-        fGraphemes.emplace_back(codepoints, TextRange(startPos, endPos));
+        //SkDebugf("Grapheme #%d [%d:%d)\n", fGraphemes16.size(), startPos, endPos);
+
+        // Update all the codepoints that belong to this grapheme
+        for (auto i = codepoints.start; i < codepoints.end; ++i) {
+            //SkDebugf("   [%d] = %d + %d\n", i, fCodePoints[i].fTextIndex, fCodePoints[i].fIndex);
+            fCodePoints[i].fGrapheme = fGraphemes16.size();
+        }
+
+        fGraphemes16.emplace_back(codepoints, TextRange(startPos, endPos));
         codepoints.start = codepoints.end;
     }
 }
 
+void ParagraphImpl::markGraphemes() {
+
+    // This breaker gets called only once for a paragraph so we don't have to keep it
+    TextBreaker breaker;
+    if (!breaker.initialize(this->text(), UBRK_CHARACTER)) {
+        return;
+    }
+
+    auto endPos = breaker.first();
+    while (!breaker.eof()) {
+        fGraphemes.add(endPos);
+        endPos = breaker.next();
+    }
+}
+
 // Returns a vector of bounding boxes that enclose all text between
 // start and end glyph indexes, including start and excluding end
 std::vector<TextBox> ParagraphImpl::getRectsForRange(unsigned start,
@@ -684,11 +622,14 @@
                                                      RectWidthStyle rectWidthStyle) {
     std::vector<TextBox> results;
     if (fText.isEmpty()) {
-        results.emplace_back(SkRect::MakeXYWH(0, 0, 0, fHeight), fParagraphStyle.getTextDirection());
+        if (start == 0 && end > 0) {
+            // On account of implied "\n" that is always at the end of the text
+            results.emplace_back(SkRect::MakeXYWH(0, 0, 0, fHeight), fParagraphStyle.getTextDirection());
+        }
         return results;
     }
 
-    markGraphemes();
+    markGraphemes16();
 
     if (start >= end || start > fCodePoints.size() || end == 0) {
         return results;
@@ -696,27 +637,34 @@
 
     // Snap text edges to the code points/grapheme edges
     TextRange text(fText.size(), fText.size());
-    if (end < fCodePoints.size()) {
-        text.end = fCodePoints[end].fTextIndex;
-        auto endGrapheme = fGraphemes[fCodePoints[end].fGrapeme];
-        if (text.end < endGrapheme.fTextRange.end) {
-            text.end = endGrapheme.fTextRange.start;
-        }
-    }
+
     if (start < fCodePoints.size()) {
-        text.start = fCodePoints[start].fTextIndex;
-        auto startGrapheme = fGraphemes[fCodePoints[start].fGrapeme];
-        if (startGrapheme.fTextRange.end <= text.end) {
-            // TODO: remove the change that is done to pass txtlib unittests
-            //  (GetRectsForRangeIncludeCombiningCharacter). Must be removed...
-            if (startGrapheme.fCodepointRange.end - start == 1 ||
-                startGrapheme.fCodepointRange.start == start) {
+        auto startGrapheme = fGraphemes16[fCodePoints[start].fGrapheme];
+        auto lastGrapheme = fCodePoints[start].fGrapheme == fGraphemes16.size() - 1;
+        if (start > startGrapheme.fCodepointRange.start) {
+            if (end == startGrapheme.fCodepointRange.end &&
+                start == startGrapheme.fCodepointRange.end - 1) {
+                // This is a fix to make test GetRectsForRangeIncludeCombiningCharacter work
+                // Must be removed...
                 text.start = startGrapheme.fTextRange.start;
             } else {
-                text.start = startGrapheme.fTextRange.end;
+                text.start  = lastGrapheme && end >= fCodePoints.size()
+                        ? fCodePoints.back().fTextIndex
+                        : startGrapheme.fTextRange.end;
             }
-        } else if (text.start > startGrapheme.fTextRange.start) {
-            text.start = startGrapheme.fTextRange.end;
+        } else {
+            text.start = startGrapheme.fTextRange.start;
+        }
+    }
+
+    if (end < fCodePoints.size()) {
+        auto codepoint = fCodePoints[end];
+        auto endGrapheme = fGraphemes16[fCodePoints[end].fGrapheme];
+        if (text.start == endGrapheme.fTextRange.start &&
+            end + codepoint.fIndex == fCodePoints.size()) {
+            text.end = endGrapheme.fTextRange.end;
+        } else {
+            text.end  = endGrapheme.fTextRange.start;
         }
     }
 
@@ -877,12 +825,7 @@
 std::vector<TextBox> ParagraphImpl::getRectsForPlaceholders() {
   std::vector<TextBox> boxes;
   if (fText.isEmpty()) {
-      boxes.emplace_back(SkRect::MakeXYWH(0, 0, 0, fHeight), fParagraphStyle.getTextDirection());
-      return boxes;
-  }
-  if (fPlaceholders.size() <= 1) {
-      boxes.emplace_back(SkRect::MakeXYWH(0, 0, 0, fHeight), fParagraphStyle.getTextDirection());
-      return boxes;
+       return boxes;
   }
   for (auto& line : fLines) {
       line.iterateThroughVisualRuns(
@@ -913,6 +856,7 @@
 
   return boxes;
 }
+
 // TODO: Deal with RTL here
 PositionWithAffinity ParagraphImpl::getGlyphPositionAtCoordinate(SkScalar dx, SkScalar dy) {
 
@@ -921,7 +865,7 @@
         return result;
     }
 
-    markGraphemes();
+    markGraphemes16();
     for (auto& line : fLines) {
         // Let's figure out if we can stop looking
         auto offsetY = line.offset().fY;
@@ -980,7 +924,7 @@
                     [](const Codepoint& lhs,size_t rhs) -> bool { return lhs.fTextIndex < rhs; });
 
                 auto codepointIndex = codepoint - fCodePoints.begin();
-                auto codepoints = fGraphemes[codepoint->fGrapeme].fCodepointRange;
+                auto codepoints = fGraphemes16[codepoint->fGrapheme].fCodepointRange;
                 auto graphemeSize = codepoints.width();
 
                 // We only need to inspect one glyph (maybe not even the entire glyph)
@@ -1111,9 +1055,9 @@
     return fTextStyles[blockIndex];
 }
 
+// TODO: Cache this information
 void ParagraphImpl::resetRunShifts() {
-    fRunShifts.reset();
-    fRunShifts.push_back_n(fRuns.size(), RunShifts());
+    fRunShifts.resize(fRuns.size());
     for (size_t i = 0; i < fRuns.size(); ++i) {
         fRunShifts[i].fShifts.push_back_n(fRuns[i].size() + 1, 0.0);
     }
@@ -1136,7 +1080,7 @@
         case kLineBroken:
             this->resetContext();
             this->resolveStrut();
-            this->resetRunShifts();
+            this->fRunShifts.reset();
             fLines.reset();
         case kFormatted:
             fPicture = nullptr;
@@ -1152,9 +1096,9 @@
 
   auto defaultTextStyle = paragraphStyle().getTextStyle();
 
-  auto typeface = fontCollection()->matchTypeface(
-          defaultTextStyle.getFontFamilies().front().c_str(), defaultTextStyle.getFontStyle(),
-          defaultTextStyle.getLocale());
+  auto typefaces = fontCollection()->findTypefaces(
+      defaultTextStyle.getFontFamilies(), defaultTextStyle.getFontStyle());
+  auto typeface = typefaces.size() ? typefaces.front() : nullptr;
 
   SkFont font(typeface, defaultTextStyle.getFontSize());
   InternalLineMetrics metrics(font, paragraphStyle().getStrutStyle().getForceStrutHeight());
diff --git a/modules/skparagraph/src/ParagraphImpl.h b/modules/skparagraph/src/ParagraphImpl.h
index 5630698..eb79275 100644
--- a/modules/skparagraph/src/ParagraphImpl.h
+++ b/modules/skparagraph/src/ParagraphImpl.h
@@ -12,7 +12,6 @@
 #include "modules/skparagraph/include/Paragraph.h"
 #include "modules/skparagraph/include/ParagraphStyle.h"
 #include "modules/skparagraph/include/TextStyle.h"
-#include "modules/skparagraph/src/FontResolver.h"
 #include "modules/skparagraph/src/Run.h"
 #include "modules/skparagraph/src/TextLine.h"
 
@@ -42,6 +41,14 @@
     TStyle fStyle;
 };
 
+struct ResolvedFontDescriptor {
+
+    ResolvedFontDescriptor(TextIndex index, SkFont font)
+        : fFont(font), fTextStart(index) { }
+    SkFont fFont;
+    TextIndex fTextStart;
+};
+
 class TextBreaker {
 public:
     TextBreaker() : fInitialized(false), fPos(-1) {}
@@ -118,7 +125,6 @@
     SkSpan<const char> text() const { return SkSpan<const char>(fText.c_str(), fText.size()); }
     InternalState state() const { return fState; }
     SkSpan<Run> runs() { return SkSpan<Run>(fRuns.data(), fRuns.size()); }
-    const SkTArray<FontDescr>& switches() const { return fFontResolver.switches(); }
     SkSpan<Block> styles() {
         return SkSpan<Block>(fTextStyles.data(), fTextStyles.size());
     }
@@ -128,11 +134,19 @@
     sk_sp<FontCollection> fontCollection() const { return fFontCollection; }
     void formatLines(SkScalar maxWidth);
 
-    void shiftCluster(ClusterIndex index, SkScalar shift) {
+    void shiftCluster(ClusterIndex index, SkScalar shift, SkScalar lastShift) {
         auto& cluster = fClusters[index];
-        auto& run = fRunShifts[cluster.runIndex()];
-        for (size_t pos = cluster.startPos(); pos < cluster.endPos(); ++pos) {
-            run.fShifts[pos] = shift;
+        auto& runShift = fRunShifts[cluster.runIndex()];
+        auto& run = fRuns[cluster.runIndex()];
+        auto start = cluster.startPos();
+        auto end = cluster.endPos();
+        if (!run.leftToRight()) {
+            runShift.fShifts[start] = lastShift;
+            ++start;
+            ++end;
+        }
+        for (size_t pos = start; pos < end; ++pos) {
+            runShift.fShifts[pos] = shift;
         }
     }
 
@@ -141,8 +155,6 @@
         return fRunShifts[index].fShifts[pos];
     }
 
-    SkScalar lineShift(size_t index) { return fLines[index].shift(); }
-
     bool strutEnabled() const { return paragraphStyle().getStrutStyle().getStrutEnabled(); }
     bool strutForceHeight() const {
         return paragraphStyle().getStrutStyle().getForceStrutHeight();
@@ -152,27 +164,6 @@
     }
     InternalLineMetrics strutMetrics() const { return fStrutMetrics; }
 
-    Measurement measurement() {
-        return {
-            fAlphabeticBaseline,
-            fIdeographicBaseline,
-            fHeight,
-            fWidth,
-            fMaxIntrinsicWidth,
-            fMinIntrinsicWidth,
-            fLongestLine,
-        };
-    }
-    void setMeasurement(Measurement m) {
-        fAlphabeticBaseline = m.fAlphabeticBaseline;
-        fIdeographicBaseline = m.fIdeographicBaseline;
-        fHeight = m.fHeight;
-        fWidth = m.fWidth;
-        fMaxIntrinsicWidth = m.fMaxIntrinsicWidth;
-        fMinIntrinsicWidth = m.fMinIntrinsicWidth;
-        fLongestLine = m.fLongestLine;
-    }
-
     SkSpan<const char> text(TextRange textRange);
     SkSpan<Cluster> clusters(ClusterRange clusterRange);
     Cluster& cluster(ClusterIndex clusterIndex);
@@ -180,15 +171,15 @@
     Run& runByCluster(ClusterIndex clusterIndex);
     SkSpan<Block> blocks(BlockRange blockRange);
     Block& block(BlockIndex blockIndex);
+    SkTArray<ResolvedFontDescriptor> resolvedFonts() const { return fFontSwitches; }
 
     void markDirty() override { fState = kUnknown; }
-    FontResolver& getResolver() { return fFontResolver; }
+
+    int32_t unresolvedGlyphs() override;
+
     void setState(InternalState state);
     sk_sp<SkPicture> getPicture() { return fPicture; }
-
-    using ShapeVisitor =
-            std::function<SkScalar(SkSpan<const char>, SkSpan<Block>, SkScalar&, size_t)>;
-    bool iterateThroughShapingRegions(ShapeVisitor shape);
+    SkRect getBoundaries() const { return fOrigin; }
 
     void resetContext();
     void resolveStrut();
@@ -215,10 +206,13 @@
     friend class ParagraphCache;
 
     friend class TextWrapper;
+    friend class OneLineShaper;
 
+    void calculateBoundaries(ClusterRange clusters, SkVector offset, SkVector advance);
     BlockRange findAllBlocks(TextRange textRange);
     void extractStyles();
 
+    void markGraphemes16();
     void markGraphemes();
 
     // Input
@@ -234,22 +228,25 @@
 
     // Internal structures
     InternalState fState;
-    SkTArray<Run> fRuns;                // kShaped
+    SkTArray<Run, false> fRuns;                // kShaped
     SkTArray<Cluster, true> fClusters;  // kClusterized (cached: text, word spacing, letter spacing, resolved fonts)
-    SkTArray<Grapheme, true> fGraphemes;
+    SkTArray<Grapheme, true> fGraphemes16;
     SkTArray<Codepoint, true> fCodePoints;
+    SkTHashSet<size_t> fGraphemes;
+    size_t fUnresolvedGlyphs;
 
-    SkTArray<RunShifts, true> fRunShifts;
+    SkTArray<RunShifts, false> fRunShifts;
     SkTArray<TextLine, true> fLines;    // kFormatted   (cached: width, max lines, ellipsis, text align)
     sk_sp<SkPicture> fPicture;          // kRecorded    (cached: text styles)
 
+    SkTArray<ResolvedFontDescriptor> fFontSwitches;
+
     InternalLineMetrics fStrutMetrics;
-    FontResolver fFontResolver;
 
     SkScalar fOldWidth;
     SkScalar fOldHeight;
     SkScalar fMaxWidthWithTrailingSpaces;
-
+    SkRect fOrigin;
     std::vector<size_t> fWords;
 };
 }  // namespace textlayout
diff --git a/modules/skparagraph/src/Run.cpp b/modules/skparagraph/src/Run.cpp
index 0fbcc07..8d9ad08 100644
--- a/modules/skparagraph/src/Run.cpp
+++ b/modules/skparagraph/src/Run.cpp
@@ -26,7 +26,7 @@
         : fMaster(master)
         , fTextRange(firstChar + info.utf8Range.begin(), firstChar + info.utf8Range.end())
         , fClusterRange(EMPTY_CLUSTERS)
-        , fFirstChar(firstChar) {
+        , fClusterStart(firstChar) {
     fFont = info.fFont;
     fHeightMultiplier = lineHeight;
     fBidiLevel = info.fBidiLevel;
@@ -35,49 +35,56 @@
     fUtf8Range = info.utf8Range;
     fOffset = SkVector::Make(offsetX, 0);
     fGlyphs.push_back_n(info.glyphCount);
+    fBounds.push_back_n(info.glyphCount);
     fPositions.push_back_n(info.glyphCount + 1);
-    fOffsets.push_back_n(info.glyphCount + 1, 0.0);
+    fOffsets.push_back_n(info.glyphCount + 1);
     fClusterIndexes.push_back_n(info.glyphCount + 1);
+    fShifts.push_back_n(info.glyphCount + 1, 0.0);
     info.fFont.getMetrics(&fFontMetrics);
     fSpaced = false;
     // To make edge cases easier:
     fPositions[info.glyphCount] = fOffset + fAdvance;
-    fClusterIndexes[info.glyphCount] = info.utf8Range.end();
+    fOffsets[info.glyphCount] = { 0, 0};
+    fClusterIndexes[info.glyphCount] = this->leftToRight() ? info.utf8Range.end() : info.utf8Range.begin();
     fEllipsis = false;
+    fPlaceholder = nullptr;
 }
 
 SkShaper::RunHandler::Buffer Run::newRunBuffer() {
-    return {fGlyphs.data(), fPositions.data(), nullptr, fClusterIndexes.data(), fOffset};
+    return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusterIndexes.data(), fOffset};
 }
 
+void Run::commit() {
+    fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
+}
 SkScalar Run::calculateWidth(size_t start, size_t end, bool clip) const {
     SkASSERT(start <= end);
     // clip |= end == size();  // Clip at the end of the run?
-    SkScalar offset = 0;
+    SkScalar shift = 0;
     if (fSpaced && end > start) {
-        offset = fOffsets[clip ? end - 1 : end] - fOffsets[start];
+        shift = fShifts[clip ? end - 1 : end] - fShifts[start];
     }
-    auto correction = end > start ? fMaster->posShift(fIndex, end - 1) - fMaster->posShift(fIndex, start) : 0;
-    return fPositions[end].fX - fPositions[start].fX + offset + correction;
+    auto correction = 0.0f;
+    if (end > start) {
+        correction = fMaster->posShift(fIndex, clip ? end - 1 : end) -
+                     fMaster->posShift(fIndex, start);
+    }
+    return posX(end) - posX(start) + shift + correction;
 }
 
-void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector offset) const {
+void Run::copyTo(SkTextBlobBuilder& builder, size_t pos, size_t size, SkVector runOffset) const {
     SkASSERT(pos + size <= this->size());
     const auto& blobBuffer = builder.allocRunPos(fFont, SkToInt(size));
     sk_careful_memcpy(blobBuffer.glyphs, fGlyphs.data() + pos, size * sizeof(SkGlyphID));
-
-    if (fSpaced || offset.fX != 0 || offset.fY != 0) {
-        for (size_t i = 0; i < size; ++i) {
-            auto point = fPositions[i + pos];
-            if (fSpaced) {
-                point.fX += fOffsets[i + pos];
-            }
-            point.fX += fMaster->posShift(fIndex, i + pos);
-            blobBuffer.points()[i] = point + offset;
+    for (size_t i = 0; i < size; ++i) {
+        auto point = fPositions[i + pos];
+        auto offset = fOffsets[i + pos];
+        point.offset(offset.fX, offset.fY);
+        if (fSpaced) {
+            point.fX += fShifts[i + pos];
         }
-    } else {
-        // Good for the first line
-        sk_careful_memcpy(blobBuffer.points(), fPositions.data() + pos, size * sizeof(SkPoint));
+        point.fX += fMaster->posShift(fIndex, i + pos);
+        blobBuffer.points()[i] = point + runOffset;
     }
 }
 
@@ -147,8 +154,8 @@
 
             visitor(start,
                     glyph,
-                    fFirstChar + cluster,
-                    fFirstChar + nextCluster,
+                    fClusterStart + cluster,
+                    fClusterStart + nextCluster,
                     this->calculateWidth(start, glyph, glyph == size()),
                     this->calculateHeight());
 
@@ -167,8 +174,8 @@
 
             visitor(start,
                     glyph,
-                    fFirstChar + cluster,
-                    fFirstChar + nextCluster,
+                    fClusterStart + cluster,
+                    fClusterStart + nextCluster,
                     this->calculateWidth(start, glyph, glyph == 0),
                     this->calculateHeight());
 
@@ -183,7 +190,7 @@
         return 0;
     }
 
-    fOffsets[cluster->endPos() - 1] += space;
+    fShifts[cluster->endPos() - 1] += space;
     // Increment the run width
     fSpaced = true;
     fAdvance.fX += space;
@@ -197,12 +204,12 @@
     // Offset all the glyphs in the cluster
     SkScalar shift = 0;
     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
-        fOffsets[i] += shift;
+        fShifts[i] += shift;
         shift += space;
     }
     if (this->size() == cluster->endPos()) {
         // To make calculations easier
-        fOffsets[cluster->endPos()] += shift;
+        fShifts[cluster->endPos()] += shift;
     }
     // Increment the run width
     fSpaced = true;
@@ -221,11 +228,11 @@
 
     fSpaced = true;
     for (size_t i = cluster->startPos(); i < cluster->endPos(); ++i) {
-        fOffsets[i] += offset;
+        fShifts[i] += offset;
     }
     if (this->size() == cluster->endPos()) {
         // To make calculations easier
-        fOffsets[cluster->endPos()] += offset;
+        fShifts[cluster->endPos()] += offset;
     }
 }
 
@@ -329,7 +336,7 @@
 }
 
 SkScalar Run::positionX(size_t pos) const {
-    return fPositions[pos].fX + fOffsets[pos] + fMaster->posShift(fIndex, pos);
+    return posX(pos) + fShifts[pos] + fMaster->posShift(fIndex, pos);
 }
 
 Run* Cluster::run() const {
diff --git a/modules/skparagraph/src/Run.h b/modules/skparagraph/src/Run.h
index 65ed787..d58c3d7 100644
--- a/modules/skparagraph/src/Run.h
+++ b/modules/skparagraph/src/Run.h
@@ -55,6 +55,15 @@
 
     SkShaper::RunHandler::Buffer newRunBuffer();
 
+    SkScalar posX(size_t index) const {
+        return fPositions[index].fX + fOffsets[index].fX;
+    }
+    void addX(size_t index, SkScalar shift) {
+        fPositions[index].fX += shift;
+    }
+    SkScalar posY(size_t index) const {
+        return fPositions[index].fY + fOffsets[index].fY;
+    }
     size_t size() const { return fGlyphs.size(); }
     void setWidth(SkScalar width) { fAdvance.fX = width; }
     void setHeight(SkScalar height) { fAdvance.fY = height; }
@@ -143,16 +152,23 @@
     SkSpan<const SkPoint> positions() const {
         return SkSpan<const SkPoint>(fPositions.begin(), fPositions.size());
     }
+    SkSpan<const SkPoint> offsets() const {
+        return SkSpan<const SkPoint>(fOffsets.begin(), fOffsets.size());
+    }
     SkSpan<const uint32_t> clusterIndexes() const {
         return SkSpan<const uint32_t>(fClusterIndexes.begin(), fClusterIndexes.size());
     }
-    SkSpan<const SkScalar> offsets() const { return SkSpan<const SkScalar>(fOffsets.begin(), fOffsets.size()); }
+    SkSpan<const SkScalar> shifts() const { return SkSpan<const SkScalar>(fShifts.begin(), fShifts.size()); }
 
+    void commit();
+
+    SkRect getBounds(size_t pos) const { return fBounds[pos]; }
 private:
     friend class ParagraphImpl;
     friend class TextLine;
     friend class InternalLineMetrics;
     friend class ParagraphCache;
+    friend class OneLineShaper;
 
     ParagraphImpl* fMaster;
     TextRange fTextRange;
@@ -167,22 +183,26 @@
     uint8_t fBidiLevel;
     SkVector fAdvance;
     SkVector fOffset;
-    size_t fFirstChar;
+    TextIndex fClusterStart;
     SkShaper::RunHandler::Range fUtf8Range;
-    SkSTArray<128, SkGlyphID, false> fGlyphs;
+    SkSTArray<128, SkGlyphID, true> fGlyphs;
     SkSTArray<128, SkPoint, true> fPositions;
+    SkSTArray<128, SkPoint, true> fOffsets;
     SkSTArray<128, uint32_t, true> fClusterIndexes;
-    SkSTArray<128, SkScalar, true> fOffsets;  // For formatting (letter/word spacing, justification)
+    SkSTArray<128, SkRect, true> fBounds;
+
+    SkSTArray<128, SkScalar, true> fShifts;  // For formatting (letter/word spacing, justification)
     bool fSpaced;
 };
 
 struct Codepoint {
 
-  Codepoint(GraphemeIndex graphemeIndex, TextIndex textIndex)
-    : fGrapeme(graphemeIndex), fTextIndex(textIndex) { }
+  Codepoint(GraphemeIndex graphemeIndex, TextIndex textIndex, size_t index)
+    : fGrapheme(graphemeIndex), fTextIndex(textIndex), fIndex(index) { }
 
-  GraphemeIndex fGrapeme;
+  GraphemeIndex fGrapheme;
   TextIndex fTextIndex;             // Used for getGlyphPositionAtCoordinate
+  size_t fIndex;
 };
 
 struct Grapheme {
@@ -325,9 +345,6 @@
         fDescent = metrics.fDescent;
         fLeading = metrics.fLeading;
         fForceStrut = forceStrut;
-        if (fForceStrut) {
-            fHeight = fDescent - fAscent + fLeading;
-        }
     }
 
     void add(Run* run) {
@@ -339,7 +356,6 @@
         fAscent = SkTMin(fAscent, run->correctAscent());
         fDescent = SkTMax(fDescent, run->correctDescent());
         fLeading = SkTMax(fLeading, run->correctLeading());
-
     }
 
     void add(InternalLineMetrics other) {
@@ -351,7 +367,6 @@
         fAscent = 0;
         fDescent = 0;
         fLeading = 0;
-        //fForceStrut = false;
     }
 
     SkScalar delta() const { return height() - ideographicBaseline(); }
@@ -361,12 +376,10 @@
             metrics.fAscent = fAscent;
             metrics.fDescent = fDescent;
             metrics.fLeading = fLeading;
-            metrics.fHeight = fDescent - fAscent + fLeading;
         } else {
             // This is another of those flutter changes. To be removed...
             metrics.fAscent = SkTMin(metrics.fAscent, fAscent - fLeading / 2.0f);
             metrics.fDescent = SkTMax(metrics.fDescent, fDescent + fLeading / 2.0f);
-            //metrics.fLeading = SkTMax(metrics.fLeading, fLeading);
         }
     }
 
@@ -375,11 +388,7 @@
     }
 
     SkScalar height() const {
-        if (fForceStrut) {
-            return SkScalarRoundToInt(fHeight);
-        } else {
-            return SkScalarRoundToInt(fDescent - fAscent + fLeading);
-        }
+        return ::round((double)fDescent - fAscent + fLeading);
     }
 
     SkScalar alphabeticBaseline() const { return fLeading / 2 - fAscent; }
@@ -399,7 +408,6 @@
     SkScalar fDescent;
     SkScalar fLeading;
     bool fForceStrut;
-    SkScalar fHeight;
 };
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/src/TextLine.cpp b/modules/skparagraph/src/TextLine.cpp
index 3a0f90a..52dc8d9 100644
--- a/modules/skparagraph/src/TextLine.cpp
+++ b/modules/skparagraph/src/TextLine.cpp
@@ -11,16 +11,10 @@
 
 namespace skia {
 namespace textlayout {
-// TODO: deal with all the intersection functionality
-int32_t intersectedSize(TextRange a, TextRange b) {
-    if (a.empty() || b.empty()) {
-        return -1;
-    }
-    auto begin = SkTMax(a.start, b.start);
-    auto end = SkTMin(a.end, b.end);
-    return begin <= end ? SkToS32(end - begin) : -1;
-}
 
+namespace {
+
+// TODO: deal with all the intersection functionality
 TextRange intersected(const TextRange& a, const TextRange& b) {
     if (a.start == b.start && a.end == b.end) return a;
     auto begin = SkTMax(a.start, b.start);
@@ -28,6 +22,29 @@
     return end >= begin ? TextRange(begin, end) : EMPTY_TEXT;
 }
 
+SkScalar littleRound(SkScalar a) {
+    // This rounding is done to match Flutter tests. Must be removed..
+  return SkScalarRoundToScalar(a * 100.0)/100.0;
+}
+
+int compareRound(SkScalar a, SkScalar b) {
+    // There is a rounding error that gets bigger when maxWidth gets bigger
+    // Currently, with VERY long zalgo text (> 100000) on a VERY long line (> 10000)
+    // it grows bigger that this little trick can hide
+    // TODO: deal with it eventually
+    auto ra = littleRound(a);
+    auto rb = littleRound(b);
+    if (ra == rb) {
+        return 0;
+    } else if (ra < rb) {
+        return -1;
+    } else {
+        return 1;
+    }
+}
+
+}
+
 TextLine::TextLine(ParagraphImpl* master,
                    SkVector offset,
                    SkVector advance,
@@ -221,8 +238,11 @@
         paint.setColor(style.getColor());
     }
 
+    // TODO: This is the change for flutter, must be removed later
+    SkScalar correctedBaseline = SkScalarFloorToScalar(this->baseline() + 0.5);
+
     SkTextBlobBuilder builder;
-    context.run->copyTo(builder, SkToU32(context.pos), context.size, SkVector::Make(0, this->baseline()));
+    context.run->copyTo(builder, SkToU32(context.pos), context.size, SkVector::Make(0, correctedBaseline));
     canvas->save();
     if (context.clippingNeeded) {
         canvas->clipRect(context.clip);
@@ -466,11 +486,12 @@
 
         if (ghost) {
             if (leftToRight) {
-                fMaster->shiftCluster(index, ghostShift);
+                fMaster->shiftCluster(index, ghostShift, ghostShift);
             }
             return true;
         }
 
+        auto lastShift = shift;
         if (cluster->isWhitespaces()) {
             if (!whitespacePatch) {
                 shift += step;
@@ -480,7 +501,7 @@
         } else {
             whitespacePatch = false;
         }
-        fMaster->shiftCluster(index, shift);
+        fMaster->shiftCluster(index, shift, lastShift);
         return true;
     });
 
@@ -496,30 +517,46 @@
     // Go through the clusters in the reverse logical order
     // taking off cluster by cluster until the ellipsis fits
     SkScalar width = fAdvance.fX;
+    bool noWhitespace = false;
+
+    auto attachEllipsis = [&](const Cluster* cluster){
+        // Shape the ellipsis
+        Run* run = shapeEllipsis(ellipsis, cluster->run());
+        run->fClusterStart = cluster->textRange().start;
+        run->setMaster(fMaster);
+
+        // See if it fits
+        if (width + run->advance().fX > maxWidth) {
+            width -= cluster->width();
+            // Continue if it's not
+            noWhitespace = true;
+            return false;
+        }
+
+        fEllipsis = std::make_shared<Run>(*run);
+        fEllipsis->shift(width, 0);
+        fAdvance.fX = width;
+        return true;
+    };
+
     iterateThroughClustersInGlyphsOrder(
-        true, false, [this, &width, ellipsis, maxWidth](const Cluster* cluster, ClusterIndex index, bool leftToRight, bool ghost) {
+        true, false, [&](const Cluster* cluster, ClusterIndex index, bool leftToRight, bool ghost) {
             if (cluster->isWhitespaces()) {
                 width -= cluster->width();
+                noWhitespace = false;
                 return true;
-            }
-
-            // Shape the ellipsis
-            Run* run = shapeEllipsis(ellipsis, cluster->run());
-            run->fFirstChar = cluster->textRange().start;
-            run->setMaster(fMaster);
-            fEllipsis = std::make_shared<Run>(*run);
-
-            // See if it fits
-            if (width + fEllipsis->advance().fX > maxWidth) {
+            } else if (noWhitespace) {
                 width -= cluster->width();
-                // Continue if it's not
                 return true;
             }
 
-            fEllipsis->shift(width, 0);
-            fAdvance.fX = width;
-            return false;
+            return !attachEllipsis(cluster);
         });
+
+    if (!fEllipsis) {
+        // Weird situation: just the ellipsis on the line (if it fits)
+        attachEllipsis(&fMaster->cluster(clusters().start));
+    }
 }
 
 Run* TextLine::shapeEllipsis(const SkString& ellipsis, Run* run) {
@@ -572,8 +609,6 @@
                                                         SkScalar textOffsetInRunInLine,
                                                         bool includeGhostSpaces,
                                                         bool limitToClusters) const {
-    SkASSERT(intersectedSize(run->textRange(), textRange) >= 0);
-
     ClipContext result = { run, 0, run->size(), 0, SkRect::MakeEmpty(), false };
 
     if (run->placeholder() != nullptr || run->fEllipsis) {
@@ -626,9 +661,9 @@
     textStartInLine -= leftCorrection;
     result.clip.offset(textStartInLine, 0);
 
-    if (result.clip.fRight > fAdvance.fX && !includeGhostSpaces) {
-        result.clip.fRight = fAdvance.fX;
+    if (compareRound(result.clip.fRight, fAdvance.fX) > 0 && !includeGhostSpaces) {
         result.clippingNeeded = true;
+        result.clip.fRight = fAdvance.fX;
     }
 
     // The text must be aligned with the lineOffset
@@ -673,15 +708,15 @@
 }
 
 SkScalar TextLine::iterateThroughSingleRunByStyles(const Run* run,
-                                               SkScalar runOffset,
-                                               TextRange textRange,
-                                               StyleType styleType,
-                                               const RunStyleVisitor& visitor) const {
+                                                   SkScalar runOffset,
+                                                   TextRange textRange,
+                                                   StyleType styleType,
+                                                   const RunStyleVisitor& visitor) const {
 
     if (run->fEllipsis) {
         // Extra efforts to get the ellipsis text style
         ClipContext clipContext = this->measureTextInsideOneRun(run->textRange(), run, runOffset, 0, false, false);
-        TextRange testRange(run->fFirstChar, run->fFirstChar + 1);
+        TextRange testRange(run->fClusterStart, run->fClusterStart + 1);
         for (BlockIndex index = fBlockRange.start; index < fBlockRange.end; ++index) {
            auto block = fMaster->styles().begin() + index;
            auto intersect = intersected(block->fRange, testRange);
@@ -765,7 +800,10 @@
 
         const auto run = &this->fMaster->run(runIndex);
         auto lineIntersection = intersected(run->textRange(), textRange);
-
+        if (lineIntersection.width() == 0 && this->width() != 0) {
+            // TODO: deal with empty runs in a better way
+            continue;
+        }
         runOffset += width;
         if (!visitor(run, runOffset, lineIntersection, &width)) {
             return;
@@ -781,7 +819,7 @@
 
     // This is a very important assert!
     // It asserts that 2 different ways of calculation come with the same results
-    if (!includingGhostSpaces && !SkScalarNearlyEqual(runOffset, this->width())) {
+    if (!includingGhostSpaces && compareRound(runOffset, this->width()) != 0) {
         SkDebugf("ASSERT: %f != %f\n", runOffset, this->width());
         SkASSERT(false);
     }
@@ -799,14 +837,17 @@
     result.fEndIndex = fTextWithWhitespacesRange.end;
     result.fEndExcludingWhitespaces = fTextRange.end;
     result.fEndIncludingNewline = fTextWithWhitespacesRange.end; // TODO: implement
-    result.fHardBreak = fMaster->cluster(fGhostClusterRange.end).isHardBreak();
-    result.fAscent = fMaxRunMetrics.ascent();
+    // TODO: For some reason Flutter imagines a hard line break at the end of the last line.
+    //  To be removed...
+    result.fHardBreak = fMaster->cluster(fGhostClusterRange.end - 1).isHardBreak() ||
+                        fGhostClusterRange.end == fMaster->clusters().size() - 1;
+    result.fAscent = - fMaxRunMetrics.ascent();
     result.fDescent = fMaxRunMetrics.descent();
-    result.fUnscaledAscent = fMaxRunMetrics.ascent(); // TODO: implement
-    result.fHeight = fAdvance.fY;
-    result.fWidth = fAdvance.fX;
+    result.fUnscaledAscent = - fMaxRunMetrics.ascent(); // TODO: implement
+    result.fHeight = littleRound(fAdvance.fY);
+    result.fWidth = littleRound(fAdvance.fX);
     result.fLeft = fOffset.fX;
-    result.fBaseline = fMaxRunMetrics.baseline();
+    result.fBaseline = fMaxRunMetrics.baseline() + (this - fMaster->lines().begin()) * result.fHeight;
     result.fLineNumber = this - fMaster->lines().begin();
 
     // Fill out the style parts
diff --git a/modules/skparagraph/src/TextWrapper.cpp b/modules/skparagraph/src/TextWrapper.cpp
index def8cb2..a0fb581 100644
--- a/modules/skparagraph/src/TextWrapper.cpp
+++ b/modules/skparagraph/src/TextWrapper.cpp
@@ -83,7 +83,7 @@
         } else if (fClusters.width() > 0) {
             fEndLine.extend(fClusters);
             fTooLongWord = false;
-        } else if (fClip.width() > 0) {
+        } else if (fClip.width() > 0 || (fTooLongWord && fTooLongCluster)) {
             fEndLine.extend(fClip);
             fTooLongWord = false;
             fTooLongCluster = false;
@@ -112,9 +112,7 @@
             break;
         }
     }
-    if (!right || true) {
-        fEndLine.trim();
-    }
+    fEndLine.trim();
 }
 
 SkScalar TextWrapper::getClustersTrimmedWidth() {
@@ -234,9 +232,7 @@
         // TODO: keep start/end/break info for text and runs but in a better way that below
         TextRange text(fEndLine.startCluster()->textRange().start, fEndLine.endCluster()->textRange().end);
         TextRange textWithSpaces(fEndLine.startCluster()->textRange().start, startLine->textRange().start);
-        if (fEndLine.breakCluster()->isHardBreak()) {
-            textWithSpaces.end = fEndLine.breakCluster()->textRange().start;
-        } else if (startLine == end) {
+        if (startLine == end) {
             textWithSpaces.end = parent->text().size();
         }
         ClusterRange clusters(fEndLine.startCluster() - start, fEndLine.endCluster() - start + 1);
diff --git a/modules/skparagraph/src/TextWrapper.h b/modules/skparagraph/src/TextWrapper.h
index 79a034d..9bc9744 100644
--- a/modules/skparagraph/src/TextWrapper.h
+++ b/modules/skparagraph/src/TextWrapper.h
@@ -104,7 +104,8 @@
             if (fEnd.cluster() != nullptr &&
                 fEnd.cluster()->master() != nullptr &&
                 fEnd.cluster()->run() != nullptr &&
-                fEnd.cluster()->run()->placeholder() == nullptr) {
+                fEnd.cluster()->run()->placeholder() == nullptr &&
+                fWidth > 0) {
                 fWidth -= (fEnd.cluster()->width() - fEnd.cluster()->trimmedWidth(fEnd.position()));
             }
         }
@@ -136,7 +137,11 @@
     };
 
 public:
-    TextWrapper() { fLineNumber = 1; }
+    TextWrapper() {
+         fLineNumber = 1;
+         fHardLineBreak = false;
+         fExceededMaxLines = false;
+    }
 
     using AddLineToParagraph = std::function<void(TextRange text,
                                                   TextRange textWithSpaces,
diff --git a/modules/skparagraph/test.html b/modules/skparagraph/test.html
new file mode 100644
index 0000000..31616cf
--- /dev/null
+++ b/modules/skparagraph/test.html
@@ -0,0 +1,35 @@
+<!doctype html>
+
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+
+  <title>Test</title>
+  <meta name="description" content="Test">
+  <meta name="author" content="Julia">
+
+</head>
+
+<body>
+<!--
+  <div style="text-align: left; background-color: lightgray; color: darkblue; width: 50mm; height: 50mm; font-family: Roboto; font-size: 5mm; " >
+    World domination is such an ugly phrase - I prefer to call it world optimisation.
+  </div>
+
+  <div style="text-align: left; background-color: lightgray; color: darkblue; font-family: Roboto,'Noto Color Emoji','Noto Serif CJK JP'; font-size: 10mm; " >
+English English 字典 字典 😀😃😄 😀😃😄
+  </div>
+
+  <div style="text-align: left; background-color: lightgray; color: darkblue; width: 200mm; font-family: Roboto; font-size: 5mm; " >
+( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(
+ ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)(
+ ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)
+  </div>
+-->
+<div style="width:10mm; height: 100mm;"></div>
+  <div style="text-align: left; background-color: lightgray; color: darkblue;font-family: Roboto; font-size: 30mm; " >
+    >Sͬ͑̀͐̈͒̈́̋̎ͮͩ̽̓ͬ̂̆̔͗́̓ͣͧ͊ͫ͛̉͌̐̑ͪ͗̚͝҉̴͉͢k̡̊̓ͫͭͩ͂͊ͨͪͬ̑ͫ̍̌̄͛̌̂̑̂̋̊̔ͫ͛̽̑ͨ̍ͭ̓̀ͪͪ̉͐͗̌̓̃̚͟͝҉̢͏̫̞̙͇͖̮͕̗̟͕͇͚̻͈̣̻̪͉̰̲̣̫ͅͅP̴̅̍͒̿͗͗̇ͩ̃͆͌̀̽͏̧̡͕͖̝̖̼̺̰̣̬͔͖͔̼͙̞̦̫͓̘͜a̸̴̸̴̢̢̨̨̫͍͓̥̼̭̼̻̤̯̙̤̻̠͚̍̌͋̂ͦͨ̽̇͌͌͆̀̽̎͒̄ͪ̐ͦ̈ͫ͐͗̓̚̚͜ͅr͐͐ͤͫ̐ͥ͂̈́̿́ͮ̃͗̓̏ͫ̀̿͏̸̵̧́͘̕͟͝͠͞͠҉̷̧͚͢͟a̓̽̎̄͗̔͛̄̐͊͛ͫ͂͌̂̂̈̈̓̔̅̅̄͊̉́ͪ̑̄͆ͬ̍͆ͭ͋̐ͬ͏̷̵̨̢̩̹̖͓̥̳̰͔̱̬͖̙͓̙͇̀̀̕͜͟͟͢͟͜͠͡g̨̅̇ͦ͋̂ͦͨͭ̓͐͆̏̂͛̉ͧ̑ͫ̐̒͛ͫ̍̒͛́̚҉̷̨̛̛̀͜͢͞҉̩̘̲͍͎̯̹̝̭̗̱͇͉̲̱͔̯̠̹̥̻͉̲̜̤̰̪̗̺̖̺r̷͌̓̇̅ͭ̀̐̃̃ͭ͑͗̉̈̇̈́ͥ̓ͣ́ͤ͂ͤ͂̏͌̆̚҉̴̸̧̢̢̛̫͉̦̥̤̙͈͉͈͉͓̙̗̟̳̜͈̗̺̟̠̠͖͓̖̪͕̠̕̕͝ͅả̸̴̡̡̧͠͞͡͞҉̛̕͟͏̷̘̪̱͈̲͉̞̠̞̪̫͎̲̬̖̀̀͟͝͞͞͠p̛͂̈͐̚͠҉̵̸̡̢̢̩̹͙̯͖̙̙̮̥̙͚̠͔̥̭̮̞̣̪̬̥̠̖̝̥̪͎́̀̕͜͡͡ͅͅh̵̷̵̡̛ͤ̂͌̐̓̐̋̋͊̒̆̽́̀̀̀͢͠͞͞҉̷̸̢̕҉͚̯͖̫̜̞̟̠̱͉̝̲̹̼͉̟͉̩̮͔̤͖̞̭̙̹̬ͅ<
+  </div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/modules/skparagraph/utils/TestFontCollection.cpp b/modules/skparagraph/utils/TestFontCollection.cpp
index 6801675..aba76d8 100644
--- a/modules/skparagraph/utils/TestFontCollection.cpp
+++ b/modules/skparagraph/utils/TestFontCollection.cpp
@@ -8,7 +8,7 @@
 namespace skia {
 namespace textlayout {
 
-TestFontCollection::TestFontCollection(const std::string& resourceDir)
+TestFontCollection::TestFontCollection(const std::string& resourceDir, bool testOnly, bool loadFonts)
   : fResourceDir(resourceDir)
   , fFontsFound(0) {
     if (fDirs == resourceDir) {
@@ -17,22 +17,40 @@
 
     fFontProvider = sk_make_sp<TypefaceFontProvider>();
 
-    SkOSFile::Iter iter(fResourceDir.c_str());
-    SkString path;
-    while (iter.next(&path)) {
-        SkString file_path;
-        file_path.printf("%s/%s", fResourceDir.c_str(), path.c_str());
-        // fonts from data are faster (skips file overhead), so we use them here for testing
-        auto data = SkData::MakeFromFileName(file_path.c_str());
-        if (data) {
-            fFontProvider->registerTypeface(SkTypeface::MakeFromData(data));
+    if (loadFonts) {
+        SkOSFile::Iter iter(fResourceDir.c_str());
+        SkString path;
+        while (iter.next(&path)) {
+            addFontFromFile(path.c_str());
         }
     }
 
     fFontsFound = fFontProvider->countFamilies();
-    this->setTestFontManager(fFontProvider);
+    if (testOnly) {
+        this->setTestFontManager(fFontProvider);
+    } else {
+        this->setAssetFontManager(fFontProvider);
+    }
     this->disableFontFallback();
     fDirs = resourceDir;
 }
+
+bool TestFontCollection::addFontFromFile(const std::string& path, const std::string& familyName) {
+
+    SkString file_path;
+    file_path.printf("%s/%s", fResourceDir.c_str(), path.c_str());
+
+    auto data = SkData::MakeFromFileName(file_path.c_str());
+    if (!data) {
+        return false;
+    }
+    if (familyName.empty()) {
+        fFontProvider->registerTypeface(SkTypeface::MakeFromData(data));
+    } else {
+        fFontProvider->registerTypeface(SkTypeface::MakeFromData(data), SkString(familyName.c_str()));
+    }
+
+    return true;
+}
 }  // namespace textlayout
 }  // namespace skia
diff --git a/modules/skparagraph/utils/TestFontCollection.h b/modules/skparagraph/utils/TestFontCollection.h
index ca379ab..5220f05 100644
--- a/modules/skparagraph/utils/TestFontCollection.h
+++ b/modules/skparagraph/utils/TestFontCollection.h
@@ -6,10 +6,11 @@
 namespace textlayout {
 class TestFontCollection : public FontCollection {
 public:
-    TestFontCollection(const std::string& resourceDir);
+    TestFontCollection(const std::string& resourceDir, bool testOnly = false, bool loadFonts = true);
     ~TestFontCollection() = default;
 
     size_t fontsFound() const { return fFontsFound; }
+    bool addFontFromFile(const std::string& path, const std::string& familyName = "");
 
 private:
     std::string fResourceDir;
diff --git a/modules/skresources/BUILD.gn b/modules/skresources/BUILD.gn
new file mode 100644
index 0000000..cf2a64e
--- /dev/null
+++ b/modules/skresources/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2019 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../../gn/skia.gni")
+
+config("public_config") {
+  include_dirs = [ "include" ]
+}
+
+static_library("skresources") {
+  import("skresources.gni")
+  public_configs = [ ":public_config" ]
+  public = skia_skresources_public
+  sources = skia_skresources_sources
+  configs += [ "../../:skia_private" ]
+  deps = [
+    "../..:skia",
+  ]
+}
diff --git a/modules/skresources/include/SkResources.h b/modules/skresources/include/SkResources.h
new file mode 100644
index 0000000..3a5769d
--- /dev/null
+++ b/modules/skresources/include/SkResources.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkResources_DEFINED
+#define SkResources_DEFINED
+
+#include "include/core/SkData.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkString.h"
+#include "include/core/SkTypes.h"
+#include "include/private/SkMutex.h"
+#include "include/private/SkTHash.h"
+
+#include <memory>
+
+class SkAnimCodecPlayer;
+class SkImage;
+
+namespace skresources {
+
+/**
+ * Image asset proxy interface.
+ */
+class SK_API ImageAsset : public SkRefCnt {
+public:
+    /**
+     * Returns true if the image asset is animated.
+     */
+    virtual bool isMultiFrame() = 0;
+
+    /**
+     * Returns the SkImage for a given frame.
+     *
+     * If the image asset is static, getImage() is only called once, at animation load time.
+     * Otherwise, this gets invoked every time the animation time is adjusted (on every seek).
+     *
+     * Embedders should cache and serve the same SkImage whenever possible, for efficiency.
+     *
+     * @param t   Frame time code, in seconds, relative to the image layer timeline origin
+     *            (in-point).
+     */
+    virtual sk_sp<SkImage> getFrame(float t) = 0;
+};
+
+class MultiFrameImageAsset final : public ImageAsset {
+public:
+    /**
+    * By default, images are decoded on-the-fly, at rasterization time.
+    * Large images may cause jank as decoding is expensive (and can thrash internal caches).
+    *
+    * Pass |predecode| true to force-decode all images upfront, at the cost of potentially more RAM
+    * and slower animation build times.
+    */
+    static sk_sp<MultiFrameImageAsset> Make(sk_sp<SkData>, bool predecode = false);
+
+    bool isMultiFrame() override;
+
+    sk_sp<SkImage> getFrame(float t) override;
+
+private:
+    explicit MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer>, bool predecode);
+
+    std::unique_ptr<SkAnimCodecPlayer> fPlayer;
+    bool                               fPreDecode;
+
+    using INHERITED = ImageAsset;
+};
+
+/**
+ * ResourceProvider is an interface that lets rich-content modules defer loading of external
+ * resources (images, fonts, etc.) to embedding clients.
+ */
+class SK_API ResourceProvider : public SkRefCnt {
+public:
+    /**
+     * Load a generic resource (currently only nested animations) specified by |path| + |name|,
+     * and return as an SkData.
+     */
+    virtual sk_sp<SkData> load(const char[] /* resource_path */,
+                               const char[] /* resource_name */) const {
+        return nullptr;
+    }
+
+    /**
+     * Load an image asset specified by |path| + |name|, and returns the corresponding
+     * ImageAsset proxy.
+     */
+    virtual sk_sp<ImageAsset> loadImageAsset(const char[] /* resource_path */,
+                                             const char[] /* resource_name */,
+                                             const char[] /* resource_id   */) const {
+        return nullptr;
+    }
+
+    /**
+     * Load an external font and return as SkData.
+     *
+     * @param name  font name    ("fName" Lottie property)
+     * @param url   web font URL ("fPath" Lottie property)
+     *
+     * -- Note --
+     *
+     *   This mechanism assumes monolithic fonts (single data blob).  Some web font providers may
+     *   serve multiple font blobs, segmented for various unicode ranges, depending on user agent
+     *   capabilities (woff, woff2).  In that case, the embedder would need to advertise no user
+     *   agent capabilities when fetching the URL, in order to receive full font data.
+     */
+    virtual sk_sp<SkData> loadFont(const char[] /* name */,
+                                   const char[] /* url  */) const {
+        return nullptr;
+    }
+};
+
+class FileResourceProvider final : public ResourceProvider {
+public:
+    static sk_sp<FileResourceProvider> Make(SkString base_dir, bool predecode = false);
+
+    sk_sp<SkData> load(const char resource_path[], const char resource_name[]) const override;
+
+    sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
+
+private:
+    FileResourceProvider(SkString, bool);
+
+    const SkString fDir;
+    const bool     fPredecode;
+
+    using INHERITED = ResourceProvider;
+};
+
+class ResourceProviderProxyBase : public ResourceProvider {
+protected:
+    explicit ResourceProviderProxyBase(sk_sp<ResourceProvider>);
+
+    sk_sp<SkData> load(const char[], const char[]) const override;
+    sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
+    sk_sp<SkData> loadFont(const char[], const char[]) const override;
+
+private:
+    const sk_sp<ResourceProvider> fProxy;
+};
+
+class CachingResourceProvider final : public ResourceProviderProxyBase {
+public:
+    static sk_sp<CachingResourceProvider> Make(sk_sp<ResourceProvider> rp) {
+        return rp ? sk_sp<CachingResourceProvider>(new CachingResourceProvider(std::move(rp)))
+                  : nullptr;
+    }
+
+private:
+    explicit CachingResourceProvider(sk_sp<ResourceProvider>);
+
+    sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
+
+    mutable SkMutex                                 fMutex;
+    mutable SkTHashMap<SkString, sk_sp<ImageAsset>> fImageCache;
+
+    using INHERITED = ResourceProviderProxyBase;
+};
+
+class DataURIResourceProviderProxy final : public ResourceProviderProxyBase {
+public:
+    static sk_sp<DataURIResourceProviderProxy> Make(sk_sp<ResourceProvider> rp,
+                                                    bool predecode = false);
+
+private:
+    DataURIResourceProviderProxy(sk_sp<ResourceProvider>, bool);
+
+    sk_sp<ImageAsset> loadImageAsset(const char[], const char[], const char[]) const override;
+
+    const bool fPredecode;
+
+    using INHERITED = ResourceProviderProxyBase;
+};
+
+} // namespace skresources
+
+#endif // SkResources_DEFINED
diff --git a/modules/skresources/skresources.gni b/modules/skresources/skresources.gni
new file mode 100644
index 0000000..10f7069
--- /dev/null
+++ b/modules/skresources/skresources.gni
@@ -0,0 +1,12 @@
+# Copyright 2019 Google LLC
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Things are easiest for everyone if these source paths are absolute.
+_src = get_path_info("src", "abspath")
+_include = get_path_info("include", "abspath")
+
+skia_skresources_public = [ "$_include/SkResources.h" ]
+
+skia_skresources_sources = [ "$_src/SkResources.cpp" ]
diff --git a/modules/skresources/src/SkResources.cpp b/modules/skresources/src/SkResources.cpp
new file mode 100644
index 0000000..38b04d2
--- /dev/null
+++ b/modules/skresources/src/SkResources.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "modules/skresources/include/SkResources.h"
+
+#include "include/codec/SkCodec.h"
+#include "include/core/SkData.h"
+#include "include/core/SkImage.h"
+#include "include/utils/SkAnimCodecPlayer.h"
+#include "include/utils/SkBase64.h"
+#include "src/core/SkMakeUnique.h"
+#include "src/core/SkOSFile.h"
+#include "src/utils/SkOSPath.h"
+
+namespace skresources {
+
+sk_sp<MultiFrameImageAsset> MultiFrameImageAsset::Make(sk_sp<SkData> data, bool predecode) {
+    if (auto codec = SkCodec::MakeFromData(std::move(data))) {
+        return sk_sp<MultiFrameImageAsset>(
+              new MultiFrameImageAsset(skstd::make_unique<SkAnimCodecPlayer>(std::move(codec)),
+                                                                             predecode));
+    }
+
+    return nullptr;
+}
+
+MultiFrameImageAsset::MultiFrameImageAsset(std::unique_ptr<SkAnimCodecPlayer> player,
+                                           bool predecode)
+    : fPlayer(std::move(player))
+    , fPreDecode(predecode) {
+    SkASSERT(fPlayer);
+}
+
+bool MultiFrameImageAsset::isMultiFrame() {
+    return fPlayer->duration() > 0;
+}
+
+sk_sp<SkImage> MultiFrameImageAsset::getFrame(float t) {
+    auto decode = [](sk_sp<SkImage> image) {
+        SkASSERT(image->isLazyGenerated());
+
+        static constexpr size_t kMaxArea = 2048 * 2048;
+        const auto image_area = SkToSizeT(image->width() * image->height());
+
+        if (image_area > kMaxArea) {
+            // When the image is too large, decode and scale down to a reasonable size.
+            const auto scale = std::sqrt(static_cast<float>(kMaxArea) / image_area);
+            const auto info  = SkImageInfo::MakeN32Premul(scale * image->width(),
+                                                          scale * image->height());
+            SkBitmap bm;
+            if (bm.tryAllocPixels(info, info.minRowBytes()) &&
+                    image->scalePixels(bm.pixmap(),
+                                       SkFilterQuality::kMedium_SkFilterQuality,
+                                       SkImage::kDisallow_CachingHint)) {
+                image = SkImage::MakeFromBitmap(bm);
+            }
+        } else {
+            // When the image size is OK, just force-decode.
+            image = image->makeRasterImage();
+        }
+
+        return image;
+    };
+
+    fPlayer->seek(static_cast<uint32_t>(t * 1000));
+    auto frame = fPlayer->getFrame();
+
+    if (fPreDecode && frame && frame->isLazyGenerated()) {
+        frame = decode(std::move(frame));
+    }
+
+    return frame;
+}
+
+sk_sp<FileResourceProvider> FileResourceProvider::Make(SkString base_dir, bool predecode) {
+    return sk_isdir(base_dir.c_str())
+        ? sk_sp<FileResourceProvider>(new FileResourceProvider(std::move(base_dir), predecode))
+        : nullptr;
+}
+
+FileResourceProvider::FileResourceProvider(SkString base_dir, bool predecode)
+    : fDir(std::move(base_dir))
+    , fPredecode(predecode) {}
+
+sk_sp<SkData> FileResourceProvider::load(const char resource_path[],
+                                         const char resource_name[]) const {
+    const auto full_dir  = SkOSPath::Join(fDir.c_str()    , resource_path),
+               full_path = SkOSPath::Join(full_dir.c_str(), resource_name);
+    return SkData::MakeFromFileName(full_path.c_str());
+}
+
+sk_sp<ImageAsset> FileResourceProvider::loadImageAsset(const char resource_path[],
+                                                       const char resource_name[],
+                                                       const char[]) const {
+    return MultiFrameImageAsset::Make(this->load(resource_path, resource_name), fPredecode);
+}
+
+ResourceProviderProxyBase::ResourceProviderProxyBase(sk_sp<ResourceProvider> rp)
+    : fProxy(std::move(rp)) {}
+
+sk_sp<SkData> ResourceProviderProxyBase::load(const char resource_path[],
+                                              const char resource_name[]) const {
+    return fProxy ? fProxy->load(resource_path, resource_name)
+                  : nullptr;
+}
+
+sk_sp<ImageAsset> ResourceProviderProxyBase::loadImageAsset(const char rpath[],
+                                                            const char rname[],
+                                                            const char rid[]) const {
+    return fProxy ? fProxy->loadImageAsset(rpath, rname, rid)
+                  : nullptr;
+}
+
+sk_sp<SkData> ResourceProviderProxyBase::loadFont(const char name[], const char url[]) const {
+    return fProxy ? fProxy->loadFont(name, url)
+                  : nullptr;
+}
+
+CachingResourceProvider::CachingResourceProvider(sk_sp<ResourceProvider> rp)
+    : INHERITED(std::move(rp)) {}
+
+sk_sp<ImageAsset> CachingResourceProvider::loadImageAsset(const char resource_path[],
+                                                          const char resource_name[],
+                                                          const char resource_id[]) const {
+    SkAutoMutexExclusive amx(fMutex);
+
+    const SkString key(resource_id);
+    if (const auto* asset = fImageCache.find(key)) {
+        return *asset;
+    }
+
+    auto asset = this->INHERITED::loadImageAsset(resource_path, resource_name, resource_id);
+    fImageCache.set(key, asset);
+
+    return asset;
+}
+
+sk_sp<DataURIResourceProviderProxy> DataURIResourceProviderProxy::Make(sk_sp<ResourceProvider> rp,
+                                                                       bool predecode) {
+    return sk_sp<DataURIResourceProviderProxy>(
+            new DataURIResourceProviderProxy(std::move(rp), predecode));
+}
+
+DataURIResourceProviderProxy::DataURIResourceProviderProxy(sk_sp<ResourceProvider> rp,
+                                                           bool predecode)
+    : INHERITED(std::move(rp))
+    , fPredecode(predecode) {}
+
+sk_sp<ImageAsset> DataURIResourceProviderProxy::loadImageAsset(const char rpath[],
+                                                               const char rname[],
+                                                               const char rid[]) const {
+    // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
+    // (https://en.wikipedia.org/wiki/Data_URI_scheme)
+    static constexpr char kDataURIImagePrefix[] = "data:image/",
+                          kDataURIEncodingStr[] = ";base64,";
+
+    if (!strncmp(rname, kDataURIImagePrefix, SK_ARRAY_COUNT(kDataURIImagePrefix) - 1)) {
+        const char* encoding_start = strstr(rname + SK_ARRAY_COUNT(kDataURIImagePrefix) - 1,
+                                            kDataURIEncodingStr);
+        if (encoding_start) {
+            const char* data_start = encoding_start + SK_ARRAY_COUNT(kDataURIEncodingStr) - 1;
+
+            // TODO: SkBase64::decode ergonomics are... interesting.
+            SkBase64 b64;
+            if (SkBase64::kNoError == b64.decode(data_start, strlen(data_start))) {
+                return MultiFrameImageAsset::Make(SkData::MakeWithProc(b64.getData(),
+                                                                       b64.getDataSize(),
+                                                      [](const void* ptr, void*) {
+                                                          delete[] static_cast<const char*>(ptr);
+                                                      }, /*ctx=*/nullptr),
+                                                  fPredecode);
+            }
+        }
+    }
+
+    return this->INHERITED::loadImageAsset(rpath, rname, rid);
+}
+
+} // namespace skresources
diff --git a/modules/sksg/include/SkSGGradient.h b/modules/sksg/include/SkSGGradient.h
index 78b7227..f76c7b8 100644
--- a/modules/sksg/include/SkSGGradient.h
+++ b/modules/sksg/include/SkSGGradient.h
@@ -25,8 +25,8 @@
 class Gradient : public Shader {
 public:
     struct ColorStop {
-        SkScalar fPosition;
-        SkColor  fColor;
+        SkScalar  fPosition;
+        SkColor4f fColor;
 
         bool operator==(const ColorStop& other) const {
             return fPosition == other.fPosition && fColor == other.fColor;
@@ -39,8 +39,8 @@
 protected:
     sk_sp<SkShader> onRevalidateShader() final;
 
-    virtual sk_sp<SkShader> onMakeShader(const std::vector<SkColor>& colors,
-                                         const std::vector<SkScalar>& positions) const = 0;
+    virtual sk_sp<SkShader> onMakeShader(const std::vector<SkColor4f>& colors,
+                                         const std::vector<SkScalar >& positions) const = 0;
 
 protected:
     Gradient() = default;
@@ -62,8 +62,8 @@
     SG_ATTRIBUTE(EndPoint  , SkPoint, fEndPoint  )
 
 protected:
-    sk_sp<SkShader> onMakeShader(const std::vector<SkColor>& colors,
-                                 const std::vector<SkScalar>& positions) const override;
+    sk_sp<SkShader> onMakeShader(const std::vector<SkColor4f>&,
+                                 const std::vector<SkScalar >&) const override;
 
 private:
     LinearGradient() = default;
@@ -86,8 +86,8 @@
     SG_ATTRIBUTE(EndRadius  , SkScalar, fEndRadius  )
 
 protected:
-    sk_sp<SkShader> onMakeShader(const std::vector<SkColor>& colors,
-                                 const std::vector<SkScalar>& positions) const override;
+    sk_sp<SkShader> onMakeShader(const std::vector<SkColor4f>&,
+                                 const std::vector<SkScalar >&) const override;
 
 private:
     RadialGradient() = default;
diff --git a/modules/sksg/include/SkSGPath.h b/modules/sksg/include/SkSGPath.h
index 1135fb9..d8ffcbe 100644
--- a/modules/sksg/include/SkSGPath.h
+++ b/modules/sksg/include/SkSGPath.h
@@ -26,7 +26,20 @@
     static sk_sp<Path> Make(const SkPath& r) { return sk_sp<Path>(new Path(r)); }
 
     SG_ATTRIBUTE(Path, SkPath, fPath)
-    SG_MAPPED_ATTRIBUTE(FillType, SkPath::FillType, fPath)
+
+    // Temporarily inlined for SkPathFillType staging
+    // SG_MAPPED_ATTRIBUTE(FillType, SkPathFillType, fPath)
+
+    SkPathFillType getFillType() const {
+        return fPath.getNewFillType();
+    }
+
+    void setFillType(SkPathFillType fillType) {
+        if (fillType != fPath.getNewFillType()) {
+            fPath.setFillType(fillType);
+            this->invalidate();
+        }
+    }
 
 protected:
     void onClip(SkCanvas*, bool antiAlias) const override;
diff --git a/modules/sksg/include/SkSGRect.h b/modules/sksg/include/SkSGRect.h
index 29456a0..fa39a8f 100644
--- a/modules/sksg/include/SkSGRect.h
+++ b/modules/sksg/include/SkSGRect.h
@@ -32,8 +32,8 @@
     SG_ATTRIBUTE(R, SkScalar, fRect.fRight )
     SG_ATTRIBUTE(B, SkScalar, fRect.fBottom)
 
-    SG_MAPPED_ATTRIBUTE(Direction        , SkPath::Direction, fAttrContaier)
-    SG_MAPPED_ATTRIBUTE(InitialPointIndex, uint8_t          , fAttrContaier)
+    SG_MAPPED_ATTRIBUTE(Direction        , SkPathDirection, fAttrContaier)
+    SG_MAPPED_ATTRIBUTE(InitialPointIndex, uint8_t        , fAttrContaier)
 
 protected:
     void onClip(SkCanvas*, bool antiAlias) const override;
@@ -52,15 +52,15 @@
         uint8_t fDirection         : 1;
         uint8_t fInitialPointIndex : 2;
 
-        SkPath::Direction getDirection() const {
-            return static_cast<SkPath::Direction>(fDirection);
+        SkPathDirection getDirection() const {
+            return static_cast<SkPathDirection>(fDirection);
         }
-        void setDirection(SkPath::Direction dir) { fDirection = SkTo<uint8_t>(dir); }
+        void setDirection(SkPathDirection dir) { fDirection = SkTo<uint8_t>(dir); }
 
         uint8_t getInitialPointIndex() const { return fInitialPointIndex; }
         void setInitialPointIndex(uint8_t idx) { fInitialPointIndex = idx; }
     };
-    AttrContainer fAttrContaier = { SkPath::kCW_Direction, 0 };
+    AttrContainer fAttrContaier = { (int)SkPathDirection::kCW, 0 };
 
     using INHERITED = GeometryNode;
 };
@@ -75,7 +75,7 @@
 
     SG_ATTRIBUTE(RRect, SkRRect, fRRect)
 
-    SG_MAPPED_ATTRIBUTE(Direction        , SkPath::Direction, fAttrContaier)
+    SG_MAPPED_ATTRIBUTE(Direction        , SkPathDirection, fAttrContaier)
     SG_MAPPED_ATTRIBUTE(InitialPointIndex, uint8_t          , fAttrContaier)
 
 protected:
@@ -95,15 +95,15 @@
         uint8_t fDirection         : 1;
         uint8_t fInitialPointIndex : 2;
 
-        SkPath::Direction getDirection() const {
-            return static_cast<SkPath::Direction>(fDirection);
+        SkPathDirection getDirection() const {
+            return static_cast<SkPathDirection>(fDirection);
         }
-        void setDirection(SkPath::Direction dir) { fDirection = SkTo<uint8_t>(dir); }
+        void setDirection(SkPathDirection dir) { fDirection = SkTo<uint8_t>(dir); }
 
         uint8_t getInitialPointIndex() const { return fInitialPointIndex; }
         void setInitialPointIndex(uint8_t idx) { fInitialPointIndex = idx; }
     };
-    AttrContainer fAttrContaier = { SkPath::kCW_Direction, 0 };
+    AttrContainer fAttrContaier = { (int)SkPathDirection::kCW, 0 };
 
     using INHERITED = GeometryNode;
 };
diff --git a/modules/sksg/src/SkSGGradient.cpp b/modules/sksg/src/SkSGGradient.cpp
index 4834a9d..1ab7d29 100644
--- a/modules/sksg/src/SkSGGradient.cpp
+++ b/modules/sksg/src/SkSGGradient.cpp
@@ -17,8 +17,8 @@
         return nullptr;
     }
 
-    std::vector<SkColor>  colors;
-    std::vector<SkScalar> positions;
+    std::vector<SkColor4f> colors;
+    std::vector<SkScalar>  positions;
     colors.reserve(fColorStops.size());
     positions.reserve(fColorStops.size());
 
@@ -33,27 +33,27 @@
     return this->onMakeShader(colors, positions);
 }
 
-sk_sp<SkShader> LinearGradient::onMakeShader(const std::vector<SkColor>& colors,
-                                             const std::vector<SkScalar>& positions) const {
+sk_sp<SkShader> LinearGradient::onMakeShader(const std::vector<SkColor4f>& colors,
+                                             const std::vector<SkScalar >& positions) const {
     SkASSERT(colors.size() == positions.size());
 
     const SkPoint pts[] = { fStartPoint, fEndPoint };
-    return SkGradientShader::MakeLinear(pts, colors.data(), positions.data(), colors.size(),
-                                        this->getTileMode());
+    return SkGradientShader::MakeLinear(pts, colors.data(), nullptr, positions.data(),
+                                        SkToInt(colors.size()), this->getTileMode());
 }
 
-sk_sp<SkShader> RadialGradient::onMakeShader(const std::vector<SkColor>& colors,
-                                             const std::vector<SkScalar>& positions) const {
+sk_sp<SkShader> RadialGradient::onMakeShader(const std::vector<SkColor4f>& colors,
+                                             const std::vector<SkScalar >& positions) const {
     SkASSERT(colors.size() == positions.size());
 
     return (fStartRadius <= 0 && fStartCenter == fEndCenter)
         ? SkGradientShader::MakeRadial(fEndCenter, fEndRadius,
-                                       colors.data(), positions.data(), colors.size(),
-                                       this->getTileMode())
+                                       colors.data(), nullptr, positions.data(),
+                                       SkToInt(colors.size()), this->getTileMode())
         : SkGradientShader::MakeTwoPointConical(fStartCenter, fStartRadius,
                                                 fEndCenter, fEndRadius,
-                                                colors.data(), positions.data(), colors.size(),
-                                                this->getTileMode());
+                                                colors.data(), nullptr, positions.data(),
+                                                SkToInt(colors.size()), this->getTileMode());
 }
 
 } //namespace sksg
diff --git a/modules/sksg/src/SkSGPath.cpp b/modules/sksg/src/SkSGPath.cpp
index 9895ee6..7ca5a04 100644
--- a/modules/sksg/src/SkSGPath.cpp
+++ b/modules/sksg/src/SkSGPath.cpp
@@ -30,8 +30,8 @@
 SkRect Path::onRevalidate(InvalidationController*, const SkMatrix&) {
     SkASSERT(this->hasInval());
 
-    const auto ft = fPath.getFillType();
-    return (ft == SkPath::kWinding_FillType || ft == SkPath::kEvenOdd_FillType)
+    const auto ft = fPath.getNewFillType();
+    return (ft == SkPathFillType::kWinding || ft == SkPathFillType::kEvenOdd)
         // "Containing" fills have finite bounds.
         ? fPath.computeTightBounds()
         // Inverse fills are "infinite".
diff --git a/modules/sksg/src/SkSGPlane.cpp b/modules/sksg/src/SkSGPlane.cpp
index 122ee53..c207289 100644
--- a/modules/sksg/src/SkSGPlane.cpp
+++ b/modules/sksg/src/SkSGPlane.cpp
@@ -30,7 +30,7 @@
 
 SkPath Plane::onAsPath() const {
     SkPath path;
-    path.setFillType(SkPath::kInverseWinding_FillType);
+    path.setFillType(SkPathFillType::kInverseWinding);
 
     return path;
 }
diff --git a/modules/sksg/src/SkSGRenderNode.cpp b/modules/sksg/src/SkSGRenderNode.cpp
index 01ae80c..5c75d5d 100644
--- a/modules/sksg/src/SkSGRenderNode.cpp
+++ b/modules/sksg/src/SkSGRenderNode.cpp
@@ -142,7 +142,7 @@
         //   => T = Inv(maskCTM) x ctm
         //
         SkMatrix invMaskCTM;
-        if (fCtx.fMaskCTM.invert(&invMaskCTM)) {
+        if (mf && fCtx.fMaskCTM.invert(&invMaskCTM)) {
             const auto relative_transform = SkMatrix::Concat(invMaskCTM, ctm);
             fCtx.fMaskFilter = SkMaskFilter::MakeCompose(std::move(fCtx.fMaskFilter),
                                                          mf->makeWithMatrix(relative_transform));
diff --git a/modules/skshaper/BUILD.gn b/modules/skshaper/BUILD.gn
index a672d98..9e97ce6 100644
--- a/modules/skshaper/BUILD.gn
+++ b/modules/skshaper/BUILD.gn
@@ -6,7 +6,7 @@
 import("../../gn/skia.gni")
 
 declare_args() {
-  skia_enable_skshaper = !(is_win && is_component_build)
+  skia_enable_skshaper = true
 }
 
 if (skia_enable_skshaper) {
diff --git a/modules/skshaper/include/SkShaper.h b/modules/skshaper/include/SkShaper.h
index 0da63ee..4fec9a5 100644
--- a/modules/skshaper/include/SkShaper.h
+++ b/modules/skshaper/include/SkShaper.h
@@ -75,8 +75,8 @@
     class TrivialRunIterator : public RunIteratorSubclass {
     public:
         static_assert(std::is_base_of<RunIterator, RunIteratorSubclass>::value, "");
-        TrivialRunIterator(size_t utf8Bytes) : fEnd(utf8Bytes), fAtEnd(false) {}
-        void consume() override { fAtEnd = true; }
+        TrivialRunIterator(size_t utf8Bytes) : fEnd(utf8Bytes), fAtEnd(fEnd == 0) {}
+        void consume() override { SkASSERT(!fAtEnd); fAtEnd = true; }
         size_t endOfCurrentRun() const override { return fAtEnd ? fEnd : 0; }
         bool atEnd() const override { return fAtEnd; }
     private:
diff --git a/modules/skshaper/src/SkShaper_primitive.cpp b/modules/skshaper/src/SkShaper_primitive.cpp
index 48035f0..a72cffe 100644
--- a/modules/skshaper/src/SkShaper_primitive.cpp
+++ b/modules/skshaper/src/SkShaper_primitive.cpp
@@ -122,11 +122,20 @@
                               SkScalar width,
                               RunHandler* handler) const
 {
-    font.consume();
-    SkASSERT(font.currentFont().getTypeface());
-    bidi.consume();
-    return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
-                       width, handler);
+    SkFont skfont;
+    if (!font.atEnd()) {
+        font.consume();
+        skfont = font.currentFont();
+    } else {
+        skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
+    }
+    SkASSERT(skfont.getTypeface());
+    bool skbidi = 0;
+    if (!bidi.atEnd()) {
+        bidi.consume();
+        skbidi = (bidi.currentLevel() % 2) == 0;
+    }
+    return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
 }
 
 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
diff --git a/platform_tools/android/apps/skqp/src/main/assets/files.checksum b/platform_tools/android/apps/skqp/src/main/assets/files.checksum
index 4f578c4..0359c33 100644
--- a/platform_tools/android/apps/skqp/src/main/assets/files.checksum
+++ b/platform_tools/android/apps/skqp/src/main/assets/files.checksum
@@ -1 +1 @@
-3705757cf56c227ec4f74ee83d6f0054
+dd9ebdcd35d9da4a1e7c8b391720f23a
diff --git a/platform_tools/android/apps/skqp/src/main/assets/skqp/rendertests.txt b/platform_tools/android/apps/skqp/src/main/assets/skqp/rendertests.txt
index e23f125..ddf5937 100644
--- a/platform_tools/android/apps/skqp/src/main/assets/skqp/rendertests.txt
+++ b/platform_tools/android/apps/skqp/src/main/assets/skqp/rendertests.txt
@@ -6,6 +6,7 @@
 CubicStroke,-1
 OverStroke,-1
 PlusMergesAA,0
+SkVMBlitter,0
 aaclip,-1
 aarectmodes,-1
 aaxfermodes,-1
@@ -43,6 +44,7 @@
 backdrop_imagefilter_croprect_nested,-1
 backdrop_imagefilter_croprect_persp,0
 backdrop_imagefilter_croprect_rotated,-1
+badapple,-1
 badpaint,-1
 bezier_conic_effects,-1
 bezier_quad_effects,-1
@@ -267,6 +269,7 @@
 drrect,-1
 drrect_small_inner,-1
 dstreadshuffle,-1
+ducky_yuv_blend,-1
 emboss,-1
 emptypath,-1
 emptystroke,0
@@ -479,20 +482,6 @@
 mixershader_shadermixer,-1
 modecolorfilters,0
 morphology,-1
-multipicturedraw_biglayer_simple,0
-multipicturedraw_biglayer_tiled,0
-multipicturedraw_invpathclip_simple,0
-multipicturedraw_invpathclip_tiled,0
-multipicturedraw_noclip_simple,0
-multipicturedraw_noclip_tiled,0
-multipicturedraw_pathclip_simple,0
-multipicturedraw_pathclip_tiled,0
-multipicturedraw_rectclip_simple,0
-multipicturedraw_rectclip_tiled,0
-multipicturedraw_rrectclip_simple,0
-multipicturedraw_rrectclip_tiled,0
-multipicturedraw_sierpinski_simple,0
-multipicturedraw_sierpinski_tiled,0
 nested_aa,-1
 nested_bw,0
 nested_flipY_aa,-1
@@ -580,7 +569,7 @@
 runtime_cf_interp_1,0
 runtime_shader,0
 runtimecolorfilter,-1
-runtimecolorfilter_interpreted,-1
+runtimecolorfilter_interpreted,0
 runtimefunctions,0
 samplelocations_hwgrad_botleft,-1
 samplelocations_hwgrad_topleft,-1
diff --git a/platform_tools/android/apps/skqp/src/main/assets/skqp/unittests.txt b/platform_tools/android/apps/skqp/src/main/assets/skqp/unittests.txt
index 1e1b0d9..02cf514 100644
--- a/platform_tools/android/apps/skqp/src/main/assets/skqp/unittests.txt
+++ b/platform_tools/android/apps/skqp/src/main/assets/skqp/unittests.txt
@@ -5,6 +5,8 @@
 AsyncReadPixelsContextShutdown
 BasicDrawOpAtlas
 BlurMaskBiggerThanDest
+BulkFillRectTest
+BulkTextureRectTest
 CCPR_busyPath
 CCPR_cache_animationAtlasReuse
 CCPR_cache_deferredCleanup
@@ -105,7 +107,6 @@
 LazyProxyFailedInstantiationTest
 LazyProxyReleaseTest
 LazyProxyTest
-LegacyAsyncReadPixels
 MorphologyFilterRadiusWithMirrorCTM_Gpu
 OnFlushCallbackTest
 OpChainTest
diff --git a/platform_tools/android/skp_gen/android_skp_capture.py b/platform_tools/android/skp_gen/android_skp_capture.py
old mode 100644
new mode 100755
diff --git a/public.bzl b/public.bzl
index d0bade8..0d92a20 100644
--- a/public.bzl
+++ b/public.bzl
@@ -267,15 +267,14 @@
 def codec_srcs(limited):
     """Sources for the codecs. Excludes Raw, and Ico, Webp, and Png if limited."""
 
-    # TODO: Enable wuffs in Google3
-    exclude = ["src/codec/SkWuffsCodec.cpp", "src/codec/*Raw*.cpp"]
+    exclude = ["src/codec/*Raw*.cpp"]
     if limited:
         exclude += [
             "src/codec/*Ico*.cpp",
             "src/codec/*Webp*.cpp",
             "src/codec/*Png*",
         ]
-    return native.glob(["src/codec/*.cpp", "third_party/gif/*.cpp"], exclude = exclude)
+    return native.glob(["src/codec/*.cpp"], exclude = exclude)
 
 GL_SRCS_UNIX = struct(
     include = [
@@ -518,6 +517,7 @@
         "gm/video_decoder.cpp",
         "tests/FontMgrAndroidParserTest.cpp",  # Android-only.
         "tests/FontMgrFontConfigTest.cpp",  # FontConfig-only.
+        "tests/SkParagraphTest.cpp",  # Skipping tests for now.
         "tests/skia_test.cpp",  # Old main.
         "tools/gpu/atlastext/*",
         "tools/gpu/dawn/*",
@@ -621,7 +621,6 @@
                 "PNG_SKIP_SETJMP_CHECK",
                 "SK_BUILD_FOR_UNIX",
                 "SK_R32_SHIFT=16",
-                "SK_PDF_USE_SFNTLY",
                 "SK_HAS_PNG_LIBRARY",
                 "SK_HAS_WEBP_LIBRARY",
             ],
@@ -744,8 +743,8 @@
 
 SKOTTIE_TOOL_SRCS = [
     "modules/skottie/src/SkottieTool.cpp",
-    "modules/skottie/utils/SkottieUtils.cpp",
-    "modules/skottie/utils/SkottieUtils.h",
+    "modules/skresources/src/SkResources.cpp",
+    "modules/skresources/include/SkResources.h",
     # TODO(benjaminwagner): Add "flags" target.
     "tools/flags/CommandLineFlags.cpp",
     "tools/flags/CommandLineFlags.h",
diff --git a/resources/SkVMTest.expected b/resources/SkVMTest.expected
index c9c618f..064e75d 100644
--- a/resources/SkVMTest.expected
+++ b/resources/SkVMTest.expected
@@ -1,544 +1,482 @@
 A8 over A8
-15 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load8 arg(0)
-  v2 = to_f32 v1
-  v3 = mul_f32 v0 v2
-  v4 = load8 arg(1)
-  v5 = to_f32 v4
-  v6 = mul_f32 v0 v5
-↑ v7 = splat 3F800000 (1)
-  v8 = sub_f32 v7 v3
-  v9 = mad_f32 v6 v8 v3
-↑ v10 = splat 437F0000 (255)
-↑ v11 = splat 3F000000 (0.5)
-  v12 = mad_f32 v9 v10 v11
-  v13 = to_i32 v12
-  store8 arg(1) v13
-
-7 registers, 15 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat 3F800000 (1)
-r2 = splat 437F0000 (255)
-r3 = splat 3F000000 (0.5)
-loop:
-r4 = load8 arg(0)
-r4 = to_f32 r4
-r4 = mul_f32 r0 r4
-r5 = load8 arg(1)
-r5 = to_f32 r5
-r5 = mul_f32 r0 r5
-r6 = sub_f32 r1 r4
-r4 = mad_f32 r5 r6 r4
-r4 = mad_f32 r4 r2 r3
-r4 = to_i32 r4
-store8 arg(1) r4
-
-A8 over G8
-21 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load8 arg(1)
-  v2 = to_f32 v1
-  v3 = mul_f32 v0 v2
-  v4 = load8 arg(0)
-  v5 = to_f32 v4
-  v6 = mul_f32 v0 v5
-↑ v7 = splat 3F800000 (1)
-  v8 = sub_f32 v7 v6
-  v9 = mul_f32 v3 v8
-↑ v10 = splat 3E59B3D0 (0.21259999)
-↑ v11 = splat 3F371759 (0.71520001)
-↑ v12 = splat 3D93DD98 (0.0722)
-  v13 = mul_f32 v9 v12
-  v14 = mad_f32 v9 v11 v13
-  v15 = mad_f32 v9 v10 v14
-↑ v16 = splat 437F0000 (255)
-↑ v17 = splat 3F000000 (0.5)
-  v18 = mad_f32 v15 v16 v17
-  v19 = to_i32 v18
-  store8 arg(1) v19
-
-9 registers, 21 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat 3F800000 (1)
-r2 = splat 3E59B3D0 (0.21259999)
-r3 = splat 3F371759 (0.71520001)
-r4 = splat 3D93DD98 (0.0722)
-r5 = splat 437F0000 (255)
-r6 = splat 3F000000 (0.5)
-loop:
-r7 = load8 arg(1)
-r7 = to_f32 r7
-r7 = mul_f32 r0 r7
-r8 = load8 arg(0)
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r8 = sub_f32 r1 r8
-r8 = mul_f32 r7 r8
-r7 = mul_f32 r8 r4
-r7 = mad_f32 r8 r3 r7
-r7 = mad_f32 r8 r2 r7
-r7 = mad_f32 r7 r5 r6
-r7 = to_i32 r7
-store8 arg(1) r7
-
-A8 over RGBA_8888
-38 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load32 arg(1)
-↑ v2 = splat FF (3.5733111e-43)
-  v3 = extract v1 0 v2
-  v4 = to_f32 v3
-  v5 = mul_f32 v0 v4
-  v6 = load8 arg(0)
-  v7 = to_f32 v6
-  v8 = mul_f32 v0 v7
-↑ v9 = splat 3F800000 (1)
-  v10 = sub_f32 v9 v8
-  v11 = mul_f32 v5 v10
-↑ v12 = splat 437F0000 (255)
-↑ v13 = splat 3F000000 (0.5)
-  v14 = mad_f32 v11 v12 v13
-  v15 = to_i32 v14
-  v16 = extract v1 8 v2
-  v17 = to_f32 v16
-  v18 = mul_f32 v0 v17
-  v19 = mul_f32 v18 v10
-  v20 = mad_f32 v19 v12 v13
-  v21 = to_i32 v20
-  v22 = pack v15 v21 8
-  v23 = extract v1 16 v2
-  v24 = to_f32 v23
-  v25 = mul_f32 v0 v24
-  v26 = mul_f32 v25 v10
-  v27 = mad_f32 v26 v12 v13
-  v28 = to_i32 v27
-  v29 = extract v1 24 v2
-  v30 = to_f32 v29
-  v31 = mul_f32 v0 v30
-  v32 = mad_f32 v31 v10 v8
-  v33 = mad_f32 v32 v12 v13
-  v34 = to_i32 v33
-  v35 = pack v28 v34 8
-  v36 = pack v22 v35 16
-  store32 arg(1) v36
-
-10 registers, 38 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat FF (3.5733111e-43)
-r2 = splat 3F800000 (1)
-r3 = splat 437F0000 (255)
-r4 = splat 3F000000 (0.5)
-loop:
-r5 = load32 arg(1)
-r6 = extract r5 0 r1
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r7 = load8 arg(0)
-r7 = to_f32 r7
-r7 = mul_f32 r0 r7
-r8 = sub_f32 r2 r7
-r6 = mul_f32 r6 r8
-r6 = mad_f32 r6 r3 r4
-r6 = to_i32 r6
-r9 = extract r5 8 r1
-r9 = to_f32 r9
-r9 = mul_f32 r0 r9
-r9 = mul_f32 r9 r8
-r9 = mad_f32 r9 r3 r4
-r9 = to_i32 r9
-r9 = pack r6 r9 8
-r6 = extract r5 16 r1
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r6 = mul_f32 r6 r8
-r6 = mad_f32 r6 r3 r4
-r6 = to_i32 r6
-r5 = extract r5 24 r1
-r5 = to_f32 r5
-r5 = mul_f32 r0 r5
-r7 = mad_f32 r5 r8 r7
-r7 = mad_f32 r7 r3 r4
-r7 = to_i32 r7
-r7 = pack r6 r7 8
-r7 = pack r9 r7 16
-store32 arg(1) r7
-
-G8 over A8
 12 values:
-↑ v0 = splat 3F800000 (1)
-↑ v1 = splat 3B808081 (0.0039215689)
-  v2 = load8 arg(1)
-  v3 = to_f32 v2
-  v4 = mul_f32 v1 v3
-↑ v5 = sub_f32 v0 v0
-  v6 = mad_f32 v4 v5 v0
-↑ v7 = splat 437F0000 (255)
-↑ v8 = splat 3F000000 (0.5)
-  v9 = mad_f32 v6 v7 v8
-  v10 = to_i32 v9
+  v0 = load8 arg(0)
+  v1 = to_f32 v0
+  v2 = mul_f32 v1 3B808081 (0.0039215689)
+  v3 = load8 arg(1)
+  v4 = to_f32 v3
+  v5 = mul_f32 v4 3B808081 (0.0039215689)
+↑ v6 = splat 3F800000 (1)
+  v7 = sub_f32 v6 v2
+  v8 = mad_f32 v5 v7 v2
+  v9 = mul_f32 v8 437F0000 (255)
+  v10 = round v9
   store8 arg(1) v10
 
-6 registers, 12 instructions:
+4 registers, 12 instructions:
 r0 = splat 3F800000 (1)
-r1 = splat 3B808081 (0.0039215689)
-r2 = sub_f32 r0 r0
-r3 = splat 437F0000 (255)
-r4 = splat 3F000000 (0.5)
 loop:
-r5 = load8 arg(1)
-r5 = to_f32 r5
-r5 = mul_f32 r1 r5
-r5 = mad_f32 r5 r2 r0
-r5 = mad_f32 r5 r3 r4
-r5 = to_i32 r5
-store8 arg(1) r5
+    r1 = load8 arg(0)
+    r1 = to_f32 r1
+    r1 = mul_f32 r1 3B808081 (0.0039215689)
+    r2 = load8 arg(1)
+    r2 = to_f32 r2
+    r2 = mul_f32 r2 3B808081 (0.0039215689)
+    r3 = sub_f32 r0 r1
+    r1 = mad_f32 r2 r3 r1
+    r1 = mul_f32 r1 437F0000 (255)
+    r1 = round r1
+    store8 arg(1) r1
 
-G8 over G8
-21 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load8 arg(0)
-  v2 = to_f32 v1
-  v3 = mul_f32 v0 v2
-  v4 = load8 arg(1)
-  v5 = to_f32 v4
-  v6 = mul_f32 v0 v5
-↟ v7 = splat 3F800000 (1)
-↑ v8 = sub_f32 v7 v7
-  v9 = mad_f32 v6 v8 v3
-↑ v10 = splat 3E59B3D0 (0.21259999)
-↑ v11 = splat 3F371759 (0.71520001)
-↑ v12 = splat 3D93DD98 (0.0722)
-  v13 = mul_f32 v9 v12
-  v14 = mad_f32 v9 v11 v13
-  v15 = mad_f32 v9 v10 v14
-↑ v16 = splat 437F0000 (255)
-↑ v17 = splat 3F000000 (0.5)
-  v18 = mad_f32 v15 v16 v17
-  v19 = to_i32 v18
-  store8 arg(1) v19
-
-9 registers, 21 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat 3F800000 (1)
-r1 = sub_f32 r1 r1
-r2 = splat 3E59B3D0 (0.21259999)
-r3 = splat 3F371759 (0.71520001)
-r4 = splat 3D93DD98 (0.0722)
-r5 = splat 437F0000 (255)
-r6 = splat 3F000000 (0.5)
-loop:
-r7 = load8 arg(0)
-r7 = to_f32 r7
-r7 = mul_f32 r0 r7
-r8 = load8 arg(1)
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r7 = mad_f32 r8 r1 r7
-r8 = mul_f32 r7 r4
-r8 = mad_f32 r7 r3 r8
-r8 = mad_f32 r7 r2 r8
-r8 = mad_f32 r8 r5 r6
-r8 = to_i32 r8
-store8 arg(1) r8
-
-G8 over RGBA_8888
-38 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load8 arg(0)
-  v2 = to_f32 v1
-  v3 = mul_f32 v0 v2
-  v4 = load32 arg(1)
-↑ v5 = splat FF (3.5733111e-43)
-  v6 = extract v4 0 v5
-  v7 = to_f32 v6
-  v8 = mul_f32 v0 v7
-↑ v9 = splat 3F800000 (1)
-↑ v10 = sub_f32 v9 v9
-  v11 = mad_f32 v8 v10 v3
-↑ v12 = splat 437F0000 (255)
-↑ v13 = splat 3F000000 (0.5)
-  v14 = mad_f32 v11 v12 v13
-  v15 = to_i32 v14
-  v16 = extract v4 8 v5
-  v17 = to_f32 v16
-  v18 = mul_f32 v0 v17
-  v19 = mad_f32 v18 v10 v3
-  v20 = mad_f32 v19 v12 v13
-  v21 = to_i32 v20
-  v22 = pack v15 v21 8
-  v23 = extract v4 16 v5
-  v24 = to_f32 v23
-  v25 = mul_f32 v0 v24
-  v26 = mad_f32 v25 v10 v3
-  v27 = mad_f32 v26 v12 v13
-  v28 = to_i32 v27
-  v29 = extract v4 24 v5
-  v30 = to_f32 v29
-  v31 = mul_f32 v0 v30
-  v32 = mad_f32 v31 v10 v9
-  v33 = mad_f32 v32 v12 v13
-  v34 = to_i32 v33
-  v35 = pack v28 v34 8
-  v36 = pack v22 v35 16
-  store32 arg(1) v36
-
-10 registers, 38 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat FF (3.5733111e-43)
-r2 = splat 3F800000 (1)
-r3 = sub_f32 r2 r2
-r4 = splat 437F0000 (255)
-r5 = splat 3F000000 (0.5)
-loop:
-r6 = load8 arg(0)
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r7 = load32 arg(1)
-r8 = extract r7 0 r1
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r8 = mad_f32 r8 r3 r6
-r8 = mad_f32 r8 r4 r5
-r8 = to_i32 r8
-r9 = extract r7 8 r1
-r9 = to_f32 r9
-r9 = mul_f32 r0 r9
-r9 = mad_f32 r9 r3 r6
-r9 = mad_f32 r9 r4 r5
-r9 = to_i32 r9
-r9 = pack r8 r9 8
-r8 = extract r7 16 r1
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r6 = mad_f32 r8 r3 r6
-r6 = mad_f32 r6 r4 r5
-r6 = to_i32 r6
-r7 = extract r7 24 r1
-r7 = to_f32 r7
-r7 = mul_f32 r0 r7
-r7 = mad_f32 r7 r3 r2
-r7 = mad_f32 r7 r4 r5
-r7 = to_i32 r7
-r7 = pack r6 r7 8
-r7 = pack r9 r7 16
-store32 arg(1) r7
-
-RGBA_8888 over A8
+A8 over G8
 17 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load32 arg(0)
-↑ v2 = splat FF (3.5733111e-43)
-  v3 = extract v1 24 v2
+  v0 = load8 arg(1)
+  v1 = to_f32 v0
+  v2 = mul_f32 v1 3B808081 (0.0039215689)
+  v3 = load8 arg(0)
   v4 = to_f32 v3
-  v5 = mul_f32 v0 v4
-  v6 = load8 arg(1)
-  v7 = to_f32 v6
-  v8 = mul_f32 v0 v7
-↑ v9 = splat 3F800000 (1)
-  v10 = sub_f32 v9 v5
-  v11 = mad_f32 v8 v10 v5
-↑ v12 = splat 437F0000 (255)
-↑ v13 = splat 3F000000 (0.5)
-  v14 = mad_f32 v11 v12 v13
-  v15 = to_i32 v14
+  v5 = mul_f32 v4 3B808081 (0.0039215689)
+↑ v6 = splat 3F800000 (1)
+  v7 = sub_f32 v6 v5
+  v8 = mul_f32 v2 v7
+↑ v9 = splat 3E59B3D0 (0.21259999)
+↑ v10 = splat 3F371759 (0.71520001)
+  v11 = mul_f32 v8 3D93DD98 (0.0722)
+  v12 = mad_f32 v8 v10 v11
+  v13 = mad_f32 v8 v9 v12
+  v14 = mul_f32 v13 437F0000 (255)
+  v15 = round v14
   store8 arg(1) v15
 
-8 registers, 17 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat FF (3.5733111e-43)
-r2 = splat 3F800000 (1)
-r3 = splat 437F0000 (255)
-r4 = splat 3F000000 (0.5)
+5 registers, 17 instructions:
+r0 = splat 3F800000 (1)
+r1 = splat 3E59B3D0 (0.21259999)
+r2 = splat 3F371759 (0.71520001)
 loop:
-r5 = load32 arg(0)
-r5 = extract r5 24 r1
-r5 = to_f32 r5
-r5 = mul_f32 r0 r5
-r6 = load8 arg(1)
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r7 = sub_f32 r2 r5
-r5 = mad_f32 r6 r7 r5
-r5 = mad_f32 r5 r3 r4
-r5 = to_i32 r5
-store8 arg(1) r5
+    r3 = load8 arg(1)
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = load8 arg(0)
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r4 = sub_f32 r0 r4
+    r4 = mul_f32 r3 r4
+    r3 = mul_f32 r4 3D93DD98 (0.0722)
+    r3 = mad_f32 r4 r2 r3
+    r3 = mad_f32 r4 r1 r3
+    r3 = mul_f32 r3 437F0000 (255)
+    r3 = round r3
+    store8 arg(1) r3
+
+A8 over RGBA_8888
+35 values:
+  v0 = load32 arg(1)
+↑ v1 = splat FF (3.5733111e-43)
+  v2 = extract v0 0 v1
+  v3 = to_f32 v2
+  v4 = mul_f32 v3 3B808081 (0.0039215689)
+  v5 = load8 arg(0)
+  v6 = to_f32 v5
+  v7 = mul_f32 v6 3B808081 (0.0039215689)
+↑ v8 = splat 3F800000 (1)
+  v9 = sub_f32 v8 v7
+  v10 = mul_f32 v4 v9
+  v11 = mul_f32 v10 437F0000 (255)
+  v12 = round v11
+  v13 = extract v0 8 v1
+  v14 = to_f32 v13
+  v15 = mul_f32 v14 3B808081 (0.0039215689)
+  v16 = mul_f32 v15 v9
+  v17 = mul_f32 v16 437F0000 (255)
+  v18 = round v17
+  v19 = pack v12 v18 8
+  v20 = extract v0 16 v1
+  v21 = to_f32 v20
+  v22 = mul_f32 v21 3B808081 (0.0039215689)
+  v23 = mul_f32 v22 v9
+  v24 = mul_f32 v23 437F0000 (255)
+  v25 = round v24
+  v26 = extract v0 24 v1
+  v27 = to_f32 v26
+  v28 = mul_f32 v27 3B808081 (0.0039215689)
+  v29 = mad_f32 v28 v9 v7
+  v30 = mul_f32 v29 437F0000 (255)
+  v31 = round v30
+  v32 = pack v25 v31 8
+  v33 = pack v19 v32 16
+  store32 arg(1) v33
+
+7 registers, 35 instructions:
+r0 = splat FF (3.5733111e-43)
+r1 = splat 3F800000 (1)
+loop:
+    r2 = load32 arg(1)
+    r3 = extract r2 0 r0
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = load8 arg(0)
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r5 = sub_f32 r1 r4
+    r3 = mul_f32 r3 r5
+    r3 = mul_f32 r3 437F0000 (255)
+    r3 = round r3
+    r6 = extract r2 8 r0
+    r6 = to_f32 r6
+    r6 = mul_f32 r6 3B808081 (0.0039215689)
+    r6 = mul_f32 r6 r5
+    r6 = mul_f32 r6 437F0000 (255)
+    r6 = round r6
+    r6 = pack r3 r6 8
+    r3 = extract r2 16 r0
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r3 = mul_f32 r3 r5
+    r3 = mul_f32 r3 437F0000 (255)
+    r3 = round r3
+    r2 = extract r2 24 r0
+    r2 = to_f32 r2
+    r2 = mul_f32 r2 3B808081 (0.0039215689)
+    r4 = mad_f32 r2 r5 r4
+    r4 = mul_f32 r4 437F0000 (255)
+    r4 = round r4
+    r4 = pack r3 r4 8
+    r4 = pack r6 r4 16
+    store32 arg(1) r4
+
+G8 over A8
+9 values:
+↑ v0 = splat 3F800000 (1)
+↑ v1 = splat 0 (0)
+  v2 = load8 arg(1)
+  v3 = to_f32 v2
+  v4 = mul_f32 v3 3B808081 (0.0039215689)
+  v5 = mad_f32 v4 v1 v0
+  v6 = mul_f32 v5 437F0000 (255)
+  v7 = round v6
+  store8 arg(1) v7
+
+3 registers, 9 instructions:
+r0 = splat 3F800000 (1)
+r1 = splat 0 (0)
+loop:
+    r2 = load8 arg(1)
+    r2 = to_f32 r2
+    r2 = mul_f32 r2 3B808081 (0.0039215689)
+    r2 = mad_f32 r2 r1 r0
+    r2 = mul_f32 r2 437F0000 (255)
+    r2 = round r2
+    store8 arg(1) r2
+
+G8 over G8
+16 values:
+  v0 = load8 arg(0)
+  v1 = to_f32 v0
+  v2 = mul_f32 v1 3B808081 (0.0039215689)
+  v3 = load8 arg(1)
+  v4 = to_f32 v3
+  v5 = mul_f32 v4 3B808081 (0.0039215689)
+↑ v6 = splat 0 (0)
+  v7 = mad_f32 v5 v6 v2
+↑ v8 = splat 3E59B3D0 (0.21259999)
+↑ v9 = splat 3F371759 (0.71520001)
+  v10 = mul_f32 v7 3D93DD98 (0.0722)
+  v11 = mad_f32 v7 v9 v10
+  v12 = mad_f32 v7 v8 v11
+  v13 = mul_f32 v12 437F0000 (255)
+  v14 = round v13
+  store8 arg(1) v14
+
+5 registers, 16 instructions:
+r0 = splat 0 (0)
+r1 = splat 3E59B3D0 (0.21259999)
+r2 = splat 3F371759 (0.71520001)
+loop:
+    r3 = load8 arg(0)
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = load8 arg(1)
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r3 = mad_f32 r4 r0 r3
+    r4 = mul_f32 r3 3D93DD98 (0.0722)
+    r4 = mad_f32 r3 r2 r4
+    r4 = mad_f32 r3 r1 r4
+    r4 = mul_f32 r4 437F0000 (255)
+    r4 = round r4
+    store8 arg(1) r4
+
+G8 over RGBA_8888
+35 values:
+  v0 = load8 arg(0)
+  v1 = to_f32 v0
+  v2 = mul_f32 v1 3B808081 (0.0039215689)
+  v3 = load32 arg(1)
+↑ v4 = splat FF (3.5733111e-43)
+  v5 = extract v3 0 v4
+  v6 = to_f32 v5
+  v7 = mul_f32 v6 3B808081 (0.0039215689)
+↑ v8 = splat 0 (0)
+  v9 = mad_f32 v7 v8 v2
+  v10 = mul_f32 v9 437F0000 (255)
+  v11 = round v10
+  v12 = extract v3 8 v4
+  v13 = to_f32 v12
+  v14 = mul_f32 v13 3B808081 (0.0039215689)
+  v15 = mad_f32 v14 v8 v2
+  v16 = mul_f32 v15 437F0000 (255)
+  v17 = round v16
+  v18 = pack v11 v17 8
+  v19 = extract v3 16 v4
+  v20 = to_f32 v19
+  v21 = mul_f32 v20 3B808081 (0.0039215689)
+  v22 = mad_f32 v21 v8 v2
+  v23 = mul_f32 v22 437F0000 (255)
+  v24 = round v23
+↑ v25 = splat 3F800000 (1)
+  v26 = extract v3 24 v4
+  v27 = to_f32 v26
+  v28 = mul_f32 v27 3B808081 (0.0039215689)
+  v29 = mad_f32 v28 v8 v25
+  v30 = mul_f32 v29 437F0000 (255)
+  v31 = round v30
+  v32 = pack v24 v31 8
+  v33 = pack v18 v32 16
+  store32 arg(1) v33
+
+7 registers, 35 instructions:
+r0 = splat FF (3.5733111e-43)
+r1 = splat 0 (0)
+r2 = splat 3F800000 (1)
+loop:
+    r3 = load8 arg(0)
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = load32 arg(1)
+    r5 = extract r4 0 r0
+    r5 = to_f32 r5
+    r5 = mul_f32 r5 3B808081 (0.0039215689)
+    r5 = mad_f32 r5 r1 r3
+    r5 = mul_f32 r5 437F0000 (255)
+    r5 = round r5
+    r6 = extract r4 8 r0
+    r6 = to_f32 r6
+    r6 = mul_f32 r6 3B808081 (0.0039215689)
+    r6 = mad_f32 r6 r1 r3
+    r6 = mul_f32 r6 437F0000 (255)
+    r6 = round r6
+    r6 = pack r5 r6 8
+    r5 = extract r4 16 r0
+    r5 = to_f32 r5
+    r5 = mul_f32 r5 3B808081 (0.0039215689)
+    r3 = mad_f32 r5 r1 r3
+    r3 = mul_f32 r3 437F0000 (255)
+    r3 = round r3
+    r4 = extract r4 24 r0
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r4 = mad_f32 r4 r1 r2
+    r4 = mul_f32 r4 437F0000 (255)
+    r4 = round r4
+    r4 = pack r3 r4 8
+    r4 = pack r6 r4 16
+    store32 arg(1) r4
+
+RGBA_8888 over A8
+14 values:
+  v0 = load32 arg(0)
+↑ v1 = splat FF (3.5733111e-43)
+  v2 = extract v0 24 v1
+  v3 = to_f32 v2
+  v4 = mul_f32 v3 3B808081 (0.0039215689)
+  v5 = load8 arg(1)
+  v6 = to_f32 v5
+  v7 = mul_f32 v6 3B808081 (0.0039215689)
+↑ v8 = splat 3F800000 (1)
+  v9 = sub_f32 v8 v4
+  v10 = mad_f32 v7 v9 v4
+  v11 = mul_f32 v10 437F0000 (255)
+  v12 = round v11
+  store8 arg(1) v12
+
+5 registers, 14 instructions:
+r0 = splat FF (3.5733111e-43)
+r1 = splat 3F800000 (1)
+loop:
+    r2 = load32 arg(0)
+    r2 = extract r2 24 r0
+    r2 = to_f32 r2
+    r2 = mul_f32 r2 3B808081 (0.0039215689)
+    r3 = load8 arg(1)
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = sub_f32 r1 r2
+    r2 = mad_f32 r3 r4 r2
+    r2 = mul_f32 r2 437F0000 (255)
+    r2 = round r2
+    store8 arg(1) r2
 
 RGBA_8888 over G8
-34 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load32 arg(0)
-↑ v2 = splat FF (3.5733111e-43)
-  v3 = extract v1 0 v2
-  v4 = to_f32 v3
-  v5 = mul_f32 v0 v4
-  v6 = load8 arg(1)
-  v7 = to_f32 v6
-  v8 = mul_f32 v0 v7
-  v9 = extract v1 24 v2
-  v10 = to_f32 v9
-  v11 = mul_f32 v0 v10
-↑ v12 = splat 3F800000 (1)
-  v13 = sub_f32 v12 v11
-  v14 = mad_f32 v8 v13 v5
-↑ v15 = splat 3E59B3D0 (0.21259999)
-  v16 = extract v1 8 v2
-  v17 = to_f32 v16
-  v18 = mul_f32 v0 v17
-  v19 = mad_f32 v8 v13 v18
-↑ v20 = splat 3F371759 (0.71520001)
-  v21 = extract v1 16 v2
-  v22 = to_f32 v21
-  v23 = mul_f32 v0 v22
-  v24 = mad_f32 v8 v13 v23
-↑ v25 = splat 3D93DD98 (0.0722)
-  v26 = mul_f32 v24 v25
-  v27 = mad_f32 v19 v20 v26
-  v28 = mad_f32 v14 v15 v27
-↑ v29 = splat 437F0000 (255)
-↑ v30 = splat 3F000000 (0.5)
-  v31 = mad_f32 v28 v29 v30
-  v32 = to_i32 v31
-  store8 arg(1) v32
+30 values:
+  v0 = load32 arg(0)
+↑ v1 = splat FF (3.5733111e-43)
+  v2 = extract v0 0 v1
+  v3 = to_f32 v2
+  v4 = mul_f32 v3 3B808081 (0.0039215689)
+  v5 = load8 arg(1)
+  v6 = to_f32 v5
+  v7 = mul_f32 v6 3B808081 (0.0039215689)
+  v8 = extract v0 24 v1
+  v9 = to_f32 v8
+  v10 = mul_f32 v9 3B808081 (0.0039215689)
+↑ v11 = splat 3F800000 (1)
+  v12 = sub_f32 v11 v10
+  v13 = mad_f32 v7 v12 v4
+↑ v14 = splat 3E59B3D0 (0.21259999)
+  v15 = extract v0 8 v1
+  v16 = to_f32 v15
+  v17 = mul_f32 v16 3B808081 (0.0039215689)
+  v18 = mad_f32 v7 v12 v17
+↑ v19 = splat 3F371759 (0.71520001)
+  v20 = extract v0 16 v1
+  v21 = to_f32 v20
+  v22 = mul_f32 v21 3B808081 (0.0039215689)
+  v23 = mad_f32 v7 v12 v22
+  v24 = mul_f32 v23 3D93DD98 (0.0722)
+  v25 = mad_f32 v18 v19 v24
+  v26 = mad_f32 v13 v14 v25
+  v27 = mul_f32 v26 437F0000 (255)
+  v28 = round v27
+  store8 arg(1) v28
 
-13 registers, 34 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat FF (3.5733111e-43)
-r2 = splat 3F800000 (1)
-r3 = splat 3E59B3D0 (0.21259999)
-r4 = splat 3F371759 (0.71520001)
-r5 = splat 3D93DD98 (0.0722)
-r6 = splat 437F0000 (255)
-r7 = splat 3F000000 (0.5)
+9 registers, 30 instructions:
+r0 = splat FF (3.5733111e-43)
+r1 = splat 3F800000 (1)
+r2 = splat 3E59B3D0 (0.21259999)
+r3 = splat 3F371759 (0.71520001)
 loop:
-r8 = load32 arg(0)
-r9 = extract r8 0 r1
-r9 = to_f32 r9
-r9 = mul_f32 r0 r9
-r10 = load8 arg(1)
-r10 = to_f32 r10
-r10 = mul_f32 r0 r10
-r11 = extract r8 24 r1
-r11 = to_f32 r11
-r11 = mul_f32 r0 r11
-r11 = sub_f32 r2 r11
-r9 = mad_f32 r10 r11 r9
-r12 = extract r8 8 r1
-r12 = to_f32 r12
-r12 = mul_f32 r0 r12
-r12 = mad_f32 r10 r11 r12
-r8 = extract r8 16 r1
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r8 = mad_f32 r10 r11 r8
-r8 = mul_f32 r8 r5
-r8 = mad_f32 r12 r4 r8
-r8 = mad_f32 r9 r3 r8
-r8 = mad_f32 r8 r6 r7
-r8 = to_i32 r8
-store8 arg(1) r8
+    r4 = load32 arg(0)
+    r5 = extract r4 0 r0
+    r5 = to_f32 r5
+    r5 = mul_f32 r5 3B808081 (0.0039215689)
+    r6 = load8 arg(1)
+    r6 = to_f32 r6
+    r6 = mul_f32 r6 3B808081 (0.0039215689)
+    r7 = extract r4 24 r0
+    r7 = to_f32 r7
+    r7 = mul_f32 r7 3B808081 (0.0039215689)
+    r7 = sub_f32 r1 r7
+    r5 = mad_f32 r6 r7 r5
+    r8 = extract r4 8 r0
+    r8 = to_f32 r8
+    r8 = mul_f32 r8 3B808081 (0.0039215689)
+    r8 = mad_f32 r6 r7 r8
+    r4 = extract r4 16 r0
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r4 = mad_f32 r6 r7 r4
+    r4 = mul_f32 r4 3D93DD98 (0.0722)
+    r4 = mad_f32 r8 r3 r4
+    r4 = mad_f32 r5 r2 r4
+    r4 = mul_f32 r4 437F0000 (255)
+    r4 = round r4
+    store8 arg(1) r4
 
 RGBA_8888 over RGBA_8888
-48 values:
-↑ v0 = splat 3B808081 (0.0039215689)
-  v1 = load32 arg(0)
-↑ v2 = splat FF (3.5733111e-43)
-  v3 = extract v1 0 v2
-  v4 = to_f32 v3
-  v5 = mul_f32 v0 v4
-  v6 = load32 arg(1)
-  v7 = extract v6 0 v2
-  v8 = to_f32 v7
-  v9 = mul_f32 v0 v8
-  v10 = extract v1 24 v2
-  v11 = to_f32 v10
-  v12 = mul_f32 v0 v11
-↑ v13 = splat 3F800000 (1)
-  v14 = sub_f32 v13 v12
-  v15 = mad_f32 v9 v14 v5
-↑ v16 = splat 437F0000 (255)
-↑ v17 = splat 3F000000 (0.5)
-  v18 = mad_f32 v15 v16 v17
-  v19 = to_i32 v18
-  v20 = extract v1 8 v2
+45 values:
+  v0 = load32 arg(0)
+↑ v1 = splat FF (3.5733111e-43)
+  v2 = extract v0 0 v1
+  v3 = to_f32 v2
+  v4 = mul_f32 v3 3B808081 (0.0039215689)
+  v5 = load32 arg(1)
+  v6 = extract v5 0 v1
+  v7 = to_f32 v6
+  v8 = mul_f32 v7 3B808081 (0.0039215689)
+  v9 = extract v0 24 v1
+  v10 = to_f32 v9
+  v11 = mul_f32 v10 3B808081 (0.0039215689)
+↑ v12 = splat 3F800000 (1)
+  v13 = sub_f32 v12 v11
+  v14 = mad_f32 v8 v13 v4
+  v15 = mul_f32 v14 437F0000 (255)
+  v16 = round v15
+  v17 = extract v0 8 v1
+  v18 = to_f32 v17
+  v19 = mul_f32 v18 3B808081 (0.0039215689)
+  v20 = extract v5 8 v1
   v21 = to_f32 v20
-  v22 = mul_f32 v0 v21
-  v23 = extract v6 8 v2
-  v24 = to_f32 v23
-  v25 = mul_f32 v0 v24
-  v26 = mad_f32 v25 v14 v22
-  v27 = mad_f32 v26 v16 v17
-  v28 = to_i32 v27
-  v29 = pack v19 v28 8
-  v30 = extract v1 16 v2
+  v22 = mul_f32 v21 3B808081 (0.0039215689)
+  v23 = mad_f32 v22 v13 v19
+  v24 = mul_f32 v23 437F0000 (255)
+  v25 = round v24
+  v26 = pack v16 v25 8
+  v27 = extract v0 16 v1
+  v28 = to_f32 v27
+  v29 = mul_f32 v28 3B808081 (0.0039215689)
+  v30 = extract v5 16 v1
   v31 = to_f32 v30
-  v32 = mul_f32 v0 v31
-  v33 = extract v6 16 v2
-  v34 = to_f32 v33
-  v35 = mul_f32 v0 v34
-  v36 = mad_f32 v35 v14 v32
-  v37 = mad_f32 v36 v16 v17
-  v38 = to_i32 v37
-  v39 = extract v6 24 v2
-  v40 = to_f32 v39
-  v41 = mul_f32 v0 v40
-  v42 = mad_f32 v41 v14 v12
-  v43 = mad_f32 v42 v16 v17
-  v44 = to_i32 v43
-  v45 = pack v38 v44 8
-  v46 = pack v29 v45 16
-  store32 arg(1) v46
+  v32 = mul_f32 v31 3B808081 (0.0039215689)
+  v33 = mad_f32 v32 v13 v29
+  v34 = mul_f32 v33 437F0000 (255)
+  v35 = round v34
+  v36 = extract v5 24 v1
+  v37 = to_f32 v36
+  v38 = mul_f32 v37 3B808081 (0.0039215689)
+  v39 = mad_f32 v38 v13 v11
+  v40 = mul_f32 v39 437F0000 (255)
+  v41 = round v40
+  v42 = pack v35 v41 8
+  v43 = pack v26 v42 16
+  store32 arg(1) v43
 
-12 registers, 48 instructions:
-r0 = splat 3B808081 (0.0039215689)
-r1 = splat FF (3.5733111e-43)
-r2 = splat 3F800000 (1)
-r3 = splat 437F0000 (255)
-r4 = splat 3F000000 (0.5)
+9 registers, 45 instructions:
+r0 = splat FF (3.5733111e-43)
+r1 = splat 3F800000 (1)
 loop:
-r5 = load32 arg(0)
-r6 = extract r5 0 r1
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r7 = load32 arg(1)
-r8 = extract r7 0 r1
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r9 = extract r5 24 r1
-r9 = to_f32 r9
-r9 = mul_f32 r0 r9
-r10 = sub_f32 r2 r9
-r6 = mad_f32 r8 r10 r6
-r6 = mad_f32 r6 r3 r4
-r6 = to_i32 r6
-r8 = extract r5 8 r1
-r8 = to_f32 r8
-r8 = mul_f32 r0 r8
-r11 = extract r7 8 r1
-r11 = to_f32 r11
-r11 = mul_f32 r0 r11
-r8 = mad_f32 r11 r10 r8
-r8 = mad_f32 r8 r3 r4
-r8 = to_i32 r8
-r8 = pack r6 r8 8
-r5 = extract r5 16 r1
-r5 = to_f32 r5
-r5 = mul_f32 r0 r5
-r6 = extract r7 16 r1
-r6 = to_f32 r6
-r6 = mul_f32 r0 r6
-r5 = mad_f32 r6 r10 r5
-r5 = mad_f32 r5 r3 r4
-r5 = to_i32 r5
-r7 = extract r7 24 r1
-r7 = to_f32 r7
-r7 = mul_f32 r0 r7
-r9 = mad_f32 r7 r10 r9
-r9 = mad_f32 r9 r3 r4
-r9 = to_i32 r9
-r9 = pack r5 r9 8
-r9 = pack r8 r9 16
-store32 arg(1) r9
+    r2 = load32 arg(0)
+    r3 = extract r2 0 r0
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r4 = load32 arg(1)
+    r5 = extract r4 0 r0
+    r5 = to_f32 r5
+    r5 = mul_f32 r5 3B808081 (0.0039215689)
+    r6 = extract r2 24 r0
+    r6 = to_f32 r6
+    r6 = mul_f32 r6 3B808081 (0.0039215689)
+    r7 = sub_f32 r1 r6
+    r3 = mad_f32 r5 r7 r3
+    r3 = mul_f32 r3 437F0000 (255)
+    r3 = round r3
+    r5 = extract r2 8 r0
+    r5 = to_f32 r5
+    r5 = mul_f32 r5 3B808081 (0.0039215689)
+    r8 = extract r4 8 r0
+    r8 = to_f32 r8
+    r8 = mul_f32 r8 3B808081 (0.0039215689)
+    r5 = mad_f32 r8 r7 r5
+    r5 = mul_f32 r5 437F0000 (255)
+    r5 = round r5
+    r5 = pack r3 r5 8
+    r2 = extract r2 16 r0
+    r2 = to_f32 r2
+    r2 = mul_f32 r2 3B808081 (0.0039215689)
+    r3 = extract r4 16 r0
+    r3 = to_f32 r3
+    r3 = mul_f32 r3 3B808081 (0.0039215689)
+    r2 = mad_f32 r3 r7 r2
+    r2 = mul_f32 r2 437F0000 (255)
+    r2 = round r2
+    r4 = extract r4 24 r0
+    r4 = to_f32 r4
+    r4 = mul_f32 r4 3B808081 (0.0039215689)
+    r6 = mad_f32 r4 r7 r6
+    r6 = mul_f32 r6 437F0000 (255)
+    r6 = round r6
+    r6 = pack r2 r6 8
+    r6 = pack r5 r6 16
+    store32 arg(1) r6
 
 I32 (Naive) 8888 over 8888
 29 values:
@@ -576,33 +514,33 @@
 r0 = splat FF (3.5733111e-43)
 r1 = splat 100 (3.5873241e-43)
 loop:
-r2 = load32 arg(0)
-r3 = extract r2 0 r0
-r4 = load32 arg(1)
-r5 = extract r4 0 r0
-r6 = extract r2 24 r0
-r7 = sub_i32 r1 r6
-r5 = mul_i32 r5 r7
-r5 = shr_i32 r5 8
-r5 = add_i32 r3 r5
-r3 = extract r2 8 r0
-r8 = extract r4 8 r0
-r8 = mul_i32 r8 r7
-r8 = shr_i32 r8 8
-r8 = add_i32 r3 r8
-r8 = pack r5 r8 8
-r2 = extract r2 16 r0
-r5 = extract r4 16 r0
-r5 = mul_i32 r5 r7
-r5 = shr_i32 r5 8
-r5 = add_i32 r2 r5
-r4 = extract r4 24 r0
-r7 = mul_i32 r4 r7
-r7 = shr_i32 r7 8
-r7 = add_i32 r6 r7
-r7 = pack r5 r7 8
-r7 = pack r8 r7 16
-store32 arg(1) r7
+    r2 = load32 arg(0)
+    r3 = extract r2 0 r0
+    r4 = load32 arg(1)
+    r5 = extract r4 0 r0
+    r6 = extract r2 24 r0
+    r7 = sub_i32 r1 r6
+    r5 = mul_i32 r5 r7
+    r5 = shr_i32 r5 8
+    r5 = add_i32 r3 r5
+    r3 = extract r2 8 r0
+    r8 = extract r4 8 r0
+    r8 = mul_i32 r8 r7
+    r8 = shr_i32 r8 8
+    r8 = add_i32 r3 r8
+    r8 = pack r5 r8 8
+    r2 = extract r2 16 r0
+    r5 = extract r4 16 r0
+    r5 = mul_i32 r5 r7
+    r5 = shr_i32 r5 8
+    r5 = add_i32 r2 r5
+    r4 = extract r4 24 r0
+    r7 = mul_i32 r4 r7
+    r7 = shr_i32 r7 8
+    r7 = add_i32 r6 r7
+    r7 = pack r5 r7 8
+    r7 = pack r8 r7 16
+    store32 arg(1) r7
 
 I32 8888 over 8888
 29 values:
@@ -640,33 +578,33 @@
 r0 = splat FF (3.5733111e-43)
 r1 = splat 100 (3.5873241e-43)
 loop:
-r2 = load32 arg(0)
-r3 = bit_and r2 r0
-r4 = load32 arg(1)
-r5 = bit_and r4 r0
-r6 = shr_i32 r2 24
-r7 = sub_i32 r1 r6
-r5 = mul_i16x2 r5 r7
-r5 = shr_i32 r5 8
-r5 = add_i32 r3 r5
-r3 = bytes r2 2
-r8 = bytes r4 2
-r8 = mul_i16x2 r8 r7
-r8 = shr_i32 r8 8
-r8 = add_i32 r3 r8
-r8 = pack r5 r8 8
-r2 = bytes r2 3
-r5 = bytes r4 3
-r5 = mul_i16x2 r5 r7
-r5 = shr_i32 r5 8
-r5 = add_i32 r2 r5
-r4 = shr_i32 r4 24
-r7 = mul_i16x2 r4 r7
-r7 = shr_i32 r7 8
-r7 = add_i32 r6 r7
-r7 = pack r5 r7 8
-r7 = pack r8 r7 16
-store32 arg(1) r7
+    r2 = load32 arg(0)
+    r3 = bit_and r2 r0
+    r4 = load32 arg(1)
+    r5 = bit_and r4 r0
+    r6 = shr_i32 r2 24
+    r7 = sub_i32 r1 r6
+    r5 = mul_i16x2 r5 r7
+    r5 = shr_i32 r5 8
+    r5 = add_i32 r3 r5
+    r3 = bytes r2 2
+    r8 = bytes r4 2
+    r8 = mul_i16x2 r8 r7
+    r8 = shr_i32 r8 8
+    r8 = add_i32 r3 r8
+    r8 = pack r5 r8 8
+    r2 = bytes r2 3
+    r5 = bytes r4 3
+    r5 = mul_i16x2 r5 r7
+    r5 = shr_i32 r5 8
+    r5 = add_i32 r2 r5
+    r4 = shr_i32 r4 24
+    r7 = mul_i16x2 r4 r7
+    r7 = shr_i32 r7 8
+    r7 = add_i32 r6 r7
+    r7 = pack r5 r7 8
+    r7 = pack r8 r7 16
+    store32 arg(1) r7
 
 I32 (SWAR) 8888 over 8888
 15 values:
@@ -690,19 +628,19 @@
 r0 = splat 1000100 (2.3510604e-38)
 r1 = splat FF00FF (2.3418409e-38)
 loop:
-r2 = load32 arg(0)
-r3 = bytes r2 404
-r3 = sub_i16x2 r0 r3
-r4 = load32 arg(1)
-r5 = bit_and r4 r1
-r5 = mul_i16x2 r5 r3
-r5 = shr_i16x2 r5 8
-r4 = shr_i16x2 r4 8
-r3 = mul_i16x2 r4 r3
-r3 = bit_clear r3 r1
-r3 = bit_or r5 r3
-r3 = add_i32 r2 r3
-store32 arg(1) r3
+    r2 = load32 arg(0)
+    r3 = bytes r2 404
+    r3 = sub_i16x2 r0 r3
+    r4 = load32 arg(1)
+    r5 = bit_and r4 r1
+    r5 = mul_i16x2 r5 r3
+    r5 = shr_i16x2 r5 8
+    r4 = shr_i16x2 r4 8
+    r3 = mul_i16x2 r4 r3
+    r3 = bit_clear r3 r1
+    r3 = bit_or r5 r3
+    r3 = add_i32 r2 r3
+    store32 arg(1) r3
 
 6 values:
 ↟ v0 = splat 1 (1.4012985e-45)
@@ -717,9 +655,9 @@
 r1 = splat 2 (2.8025969e-45)
 r1 = add_i32 r0 r1
 loop:
-r0 = load32 arg(0)
-r0 = mul_i32 r0 r1
-store32 arg(0) r0
+    r0 = load32 arg(0)
+    r0 = mul_i32 r0 r1
+    store32 arg(0) r0
 
 19 values:
 ↑ v0 = splat FF (3.5733111e-43)
@@ -745,22 +683,22 @@
 6 registers, 19 instructions:
 r0 = splat FF (3.5733111e-43)
 loop:
-r1 = load32 arg(0)
-r2 = extract r1 0 r0
-r3 = load32 arg(1)
-r4 = extract r3 0 r0
-r4 = add_i32 r2 r4
-r2 = extract r1 8 r0
-r5 = extract r3 8 r0
-r5 = add_i32 r2 r5
-r5 = pack r4 r5 8
-r4 = extract r1 16 r0
-r2 = extract r3 16 r0
-r2 = add_i32 r4 r2
-r1 = extract r1 24 r0
-r3 = extract r3 24 r0
-r3 = add_i32 r1 r3
-r3 = pack r2 r3 8
-r3 = pack r5 r3 16
-store32 arg(1) r3
+    r1 = load32 arg(0)
+    r2 = extract r1 0 r0
+    r3 = load32 arg(1)
+    r4 = extract r3 0 r0
+    r4 = add_i32 r2 r4
+    r2 = extract r1 8 r0
+    r5 = extract r3 8 r0
+    r5 = add_i32 r2 r5
+    r5 = pack r4 r5 8
+    r4 = extract r1 16 r0
+    r2 = extract r3 16 r0
+    r2 = add_i32 r4 r2
+    r1 = extract r1 24 r0
+    r3 = extract r3 24 r0
+    r3 = add_i32 r1 r3
+    r3 = pack r2 r3 8
+    r3 = pack r5 r3 16
+    store32 arg(1) r3
 
diff --git a/resources/ducky.jpg b/resources/ducky.jpg
new file mode 100644
index 0000000..f1b8657
--- /dev/null
+++ b/resources/ducky.jpg
Binary files differ
diff --git a/resources/ducky.png b/resources/ducky.png
new file mode 100644
index 0000000..f1cbd35
--- /dev/null
+++ b/resources/ducky.png
Binary files differ
diff --git a/resources/fonts/abc/abc+agrave.ttf b/resources/fonts/abc/abc+agrave.ttf
new file mode 100644
index 0000000..fab112b
--- /dev/null
+++ b/resources/fonts/abc/abc+agrave.ttf
Binary files differ
diff --git a/resources/fonts/abc/abc+agrave.ttx b/resources/fonts/abc/abc+agrave.ttx
new file mode 100644
index 0000000..54ab039
--- /dev/null
+++ b/resources/fonts/abc/abc+agrave.ttx
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.44">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="glyph00001"/>
+    <GlyphID id="2" name="a"/>
+    <GlyphID id="3" name="b"/>
+    <GlyphID id="4" name="c"/>
+    <GlyphID id="5" name="agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x225d2c87"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Mon Aug  6 13:54:50 1990"/>
+    <modified value="Thu Nov  7 20:55:14 2019"/>
+    <xMin value="37"/>
+    <yMin value="0"/>
+    <xMax value="640"/>
+    <yMax value="737"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="927"/>
+    <descent value="-217"/>
+    <lineGap value="34"/>
+    <advanceWidthMax value="768"/>
+    <minLeftSideBearing value="37"/>
+    <minRightSideBearing value="19"/>
+    <xMaxExtent value="640"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="36"/>
+    <maxCompositeContours value="3"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="16"/>
+    <maxStorage value="47"/>
+    <maxFunctionDefs value="66"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1036"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="1"/>
+    <xAvgCharWidth value="554"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="717"/>
+    <ySubscriptYSize value="666"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="142"/>
+    <ySuperscriptXSize value="717"/>
+    <ySuperscriptYSize value="666"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="488"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="265"/>
+    <sFamilyClass value="2053"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="2"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="2"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="Mono"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="224"/>
+    <sTypoAscender value="746"/>
+    <sTypoDescender value="-216"/>
+    <sTypoLineGap value="154"/>
+    <usWinAscent value="927"/>
+    <usWinDescent value="217"/>
+    <ulCodePageRange1 value="01000000 00000000 00000001 11111111"/>
+    <ulCodePageRange2 value="11111111 11111111 00000000 00000000"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="768" lsb="128"/>
+    <mtx name="a" width="569" lsb="37"/>
+    <mtx name="agrave" width="569" lsb="37"/>
+    <mtx name="b" width="569" lsb="67"/>
+    <mtx name="c" width="512" lsb="40"/>
+    <mtx name="glyph00001" width="341" lsb="44"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="c"/><!-- LATIN SMALL LETTER C -->
+      <map code="0xe0" name="agrave"/><!-- LATIN SMALL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="128" yMin="0" xMax="640" yMax="640">
+      <contour>
+        <pt x="128" y="0" on="1"/>
+        <pt x="128" y="640" on="1"/>
+        <pt x="640" y="640" on="1"/>
+        <pt x="640" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="144" y="16" on="1"/>
+        <pt x="624" y="16" on="1"/>
+        <pt x="624" y="624" on="1"/>
+        <pt x="144" y="624" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="37" yMin="0" xMax="526" yMax="543">
+      <contour>
+        <pt x="414" y="66" on="1"/>
+        <pt x="337" y="0" on="0"/>
+        <pt x="217" y="0" on="1"/>
+        <pt x="37" y="0" on="0"/>
+        <pt x="37" y="140" on="1"/>
+        <pt x="37" y="281" on="0"/>
+        <pt x="246" y="306" on="1"/>
+        <pt x="354" y="319" on="0"/>
+        <pt x="406" y="337" on="1"/>
+        <pt x="406" y="468" on="0"/>
+        <pt x="279" y="468" on="1"/>
+        <pt x="166" y="468" on="0"/>
+        <pt x="144" y="380" on="1"/>
+        <pt x="52" y="380" on="1"/>
+        <pt x="86" y="543" on="0"/>
+        <pt x="498" y="543" on="0"/>
+        <pt x="498" y="342" on="1"/>
+        <pt x="498" y="222" on="1"/>
+        <pt x="498" y="96" on="0"/>
+        <pt x="509" y="30" on="0"/>
+        <pt x="526" y="0" on="1"/>
+        <pt x="432" y="0" on="1"/>
+        <pt x="418" y="28" on="0"/>
+      </contour>
+      <contour>
+        <pt x="406" y="266" on="1"/>
+        <pt x="360" y="246" on="0"/>
+        <pt x="260" y="232" on="1"/>
+        <pt x="129" y="214" on="0"/>
+        <pt x="129" y="144" on="1"/>
+        <pt x="129" y="78" on="0"/>
+        <pt x="239" y="78" on="1"/>
+        <pt x="406" y="78" on="0"/>
+        <pt x="406" y="234" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="agrave" xMin="37" yMin="0" xMax="526" yMax="737">
+      <component glyphName="a" x="0" y="0" flags="0x1204"/>
+      <component glyphName="glyph00001" x="125" y="0" flags="0x1004"/>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="67" yMin="0" xMax="528" yMax="733">
+      <contour>
+        <pt x="157" y="0" on="1"/>
+        <pt x="67" y="0" on="1"/>
+        <pt x="67" y="733" on="1"/>
+        <pt x="157" y="733" on="1"/>
+        <pt x="157" y="472" on="1"/>
+        <pt x="214" y="543" on="0"/>
+        <pt x="302" y="543" on="1"/>
+        <pt x="528" y="543" on="0"/>
+        <pt x="528" y="274" on="1"/>
+        <pt x="528" y="0" on="0"/>
+        <pt x="157" y="0" on="0"/>
+        <pt x="157" y="66" on="1"/>
+      </contour>
+      <contour>
+        <pt x="157" y="270" on="1"/>
+        <pt x="157" y="76" on="0"/>
+        <pt x="297" y="76" on="1"/>
+        <pt x="442" y="76" on="0"/>
+        <pt x="442" y="266" on="1"/>
+        <pt x="442" y="469" on="0"/>
+        <pt x="294" y="469" on="1"/>
+        <pt x="157" y="469" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="c" xMin="40" yMin="0" xMax="493" yMax="543">
+      <contour>
+        <pt x="400" y="182" on="1"/>
+        <pt x="493" y="182" on="1"/>
+        <pt x="464" y="0" on="0"/>
+        <pt x="281" y="0" on="1"/>
+        <pt x="40" y="0" on="0"/>
+        <pt x="40" y="264" on="1"/>
+        <pt x="40" y="543" on="0"/>
+        <pt x="282" y="543" on="1"/>
+        <pt x="460" y="543" on="0"/>
+        <pt x="493" y="375" on="1"/>
+        <pt x="400" y="375" on="1"/>
+        <pt x="378" y="469" on="0"/>
+        <pt x="286" y="469" on="1"/>
+        <pt x="132" y="469" on="0"/>
+        <pt x="132" y="266" on="1"/>
+        <pt x="132" y="78" on="0"/>
+        <pt x="279" y="78" on="1"/>
+        <pt x="384" y="78" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00001" xMin="44" yMin="597" xMax="232" yMax="737">
+      <contour>
+        <pt x="232" y="597" on="1"/>
+        <pt x="160" y="597" on="1"/>
+        <pt x="44" y="737" on="1"/>
+        <pt x="165" y="737" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      abc+agrave
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      ABC+agrave-Skia-Test-Font
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      abc+agrave
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      abc+agraveNormal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-108"/>
+    <underlineThickness value="75"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/resources/fonts/abc/abc+grave.ttf b/resources/fonts/abc/abc+grave.ttf
new file mode 100644
index 0000000..c3abcd3
--- /dev/null
+++ b/resources/fonts/abc/abc+grave.ttf
Binary files differ
diff --git a/resources/fonts/abc/abc+grave.ttx b/resources/fonts/abc/abc+grave.ttx
new file mode 100644
index 0000000..e5b94ae
--- /dev/null
+++ b/resources/fonts/abc/abc+grave.ttx
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.44">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="b"/>
+    <GlyphID id="3" name="c"/>
+    <GlyphID id="4" name="gravecomb"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xeb63091d"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Mon Aug  6 13:54:50 1990"/>
+    <modified value="Thu Nov  7 20:55:30 2019"/>
+    <xMin value="-267"/>
+    <yMin value="0"/>
+    <xMax value="640"/>
+    <yMax value="914"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="927"/>
+    <descent value="-217"/>
+    <lineGap value="34"/>
+    <advanceWidthMax value="768"/>
+    <minLeftSideBearing value="-267"/>
+    <minRightSideBearing value="19"/>
+    <xMaxExtent value="640"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="5"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="16"/>
+    <maxStorage value="47"/>
+    <maxFunctionDefs value="66"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1036"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="1"/>
+    <xAvgCharWidth value="554"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="717"/>
+    <ySubscriptYSize value="666"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="142"/>
+    <ySuperscriptXSize value="717"/>
+    <ySuperscriptYSize value="666"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="488"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="265"/>
+    <sFamilyClass value="2053"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="2"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="2"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="Mono"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="768"/>
+    <sTypoAscender value="746"/>
+    <sTypoDescender value="-216"/>
+    <sTypoLineGap value="154"/>
+    <usWinAscent value="927"/>
+    <usWinDescent value="217"/>
+    <ulCodePageRange1 value="01000000 00000000 00000001 11111111"/>
+    <ulCodePageRange2 value="11111111 11111111 00000000 00000000"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="768" lsb="128"/>
+    <mtx name="a" width="569" lsb="37"/>
+    <mtx name="b" width="569" lsb="67"/>
+    <mtx name="c" width="512" lsb="40"/>
+    <mtx name="gravecomb" width="0" lsb="-267"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="c"/><!-- LATIN SMALL LETTER C -->
+      <map code="0x300" name="gravecomb"/><!-- COMBINING GRAVE ACCENT -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="128" yMin="0" xMax="640" yMax="640">
+      <contour>
+        <pt x="128" y="0" on="1"/>
+        <pt x="128" y="640" on="1"/>
+        <pt x="640" y="640" on="1"/>
+        <pt x="640" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="144" y="16" on="1"/>
+        <pt x="624" y="16" on="1"/>
+        <pt x="624" y="624" on="1"/>
+        <pt x="144" y="624" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="37" yMin="0" xMax="526" yMax="543">
+      <contour>
+        <pt x="414" y="66" on="1"/>
+        <pt x="337" y="0" on="0"/>
+        <pt x="217" y="0" on="1"/>
+        <pt x="37" y="0" on="0"/>
+        <pt x="37" y="140" on="1"/>
+        <pt x="37" y="281" on="0"/>
+        <pt x="246" y="306" on="1"/>
+        <pt x="354" y="319" on="0"/>
+        <pt x="406" y="337" on="1"/>
+        <pt x="406" y="468" on="0"/>
+        <pt x="279" y="468" on="1"/>
+        <pt x="166" y="468" on="0"/>
+        <pt x="144" y="380" on="1"/>
+        <pt x="52" y="380" on="1"/>
+        <pt x="86" y="543" on="0"/>
+        <pt x="498" y="543" on="0"/>
+        <pt x="498" y="342" on="1"/>
+        <pt x="498" y="222" on="1"/>
+        <pt x="498" y="96" on="0"/>
+        <pt x="509" y="30" on="0"/>
+        <pt x="526" y="0" on="1"/>
+        <pt x="432" y="0" on="1"/>
+        <pt x="418" y="28" on="0"/>
+      </contour>
+      <contour>
+        <pt x="406" y="266" on="1"/>
+        <pt x="360" y="246" on="0"/>
+        <pt x="260" y="232" on="1"/>
+        <pt x="129" y="214" on="0"/>
+        <pt x="129" y="144" on="1"/>
+        <pt x="129" y="78" on="0"/>
+        <pt x="239" y="78" on="1"/>
+        <pt x="406" y="78" on="0"/>
+        <pt x="406" y="234" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="67" yMin="0" xMax="528" yMax="733">
+      <contour>
+        <pt x="157" y="0" on="1"/>
+        <pt x="67" y="0" on="1"/>
+        <pt x="67" y="733" on="1"/>
+        <pt x="157" y="733" on="1"/>
+        <pt x="157" y="472" on="1"/>
+        <pt x="214" y="543" on="0"/>
+        <pt x="302" y="543" on="1"/>
+        <pt x="528" y="543" on="0"/>
+        <pt x="528" y="274" on="1"/>
+        <pt x="528" y="0" on="0"/>
+        <pt x="157" y="0" on="0"/>
+        <pt x="157" y="66" on="1"/>
+      </contour>
+      <contour>
+        <pt x="157" y="270" on="1"/>
+        <pt x="157" y="76" on="0"/>
+        <pt x="297" y="76" on="1"/>
+        <pt x="442" y="76" on="0"/>
+        <pt x="442" y="266" on="1"/>
+        <pt x="442" y="469" on="0"/>
+        <pt x="294" y="469" on="1"/>
+        <pt x="157" y="469" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="c" xMin="40" yMin="0" xMax="493" yMax="543">
+      <contour>
+        <pt x="400" y="182" on="1"/>
+        <pt x="493" y="182" on="1"/>
+        <pt x="464" y="0" on="0"/>
+        <pt x="281" y="0" on="1"/>
+        <pt x="40" y="0" on="0"/>
+        <pt x="40" y="264" on="1"/>
+        <pt x="40" y="543" on="0"/>
+        <pt x="282" y="543" on="1"/>
+        <pt x="460" y="543" on="0"/>
+        <pt x="493" y="375" on="1"/>
+        <pt x="400" y="375" on="1"/>
+        <pt x="378" y="469" on="0"/>
+        <pt x="286" y="469" on="1"/>
+        <pt x="132" y="469" on="0"/>
+        <pt x="132" y="266" on="1"/>
+        <pt x="132" y="78" on="0"/>
+        <pt x="279" y="78" on="1"/>
+        <pt x="384" y="78" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="gravecomb" xMin="-267" yMin="774" xMax="-79" yMax="914">
+      <contour>
+        <pt x="-79" y="774" on="1"/>
+        <pt x="-152" y="774" on="1"/>
+        <pt x="-267" y="914" on="1"/>
+        <pt x="-146" y="914" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      abc+grave
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      ABC+grave-Skia-Test-Font
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      abc+grave
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      abc+graveNormal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-108"/>
+    <underlineThickness value="75"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/resources/fonts/abc/abc.ttf b/resources/fonts/abc/abc.ttf
new file mode 100644
index 0000000..3dc9dfd
--- /dev/null
+++ b/resources/fonts/abc/abc.ttf
Binary files differ
diff --git a/resources/fonts/abc/abc.ttx b/resources/fonts/abc/abc.ttx
new file mode 100644
index 0000000..907eb85
--- /dev/null
+++ b/resources/fonts/abc/abc.ttx
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.44">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="b"/>
+    <GlyphID id="3" name="c"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x53b9be61"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Mon Aug  6 13:54:50 1990"/>
+    <modified value="Thu Nov  7 20:45:34 2019"/>
+    <xMin value="37"/>
+    <yMin value="0"/>
+    <xMax value="640"/>
+    <yMax value="733"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="927"/>
+    <descent value="-217"/>
+    <lineGap value="34"/>
+    <advanceWidthMax value="768"/>
+    <minLeftSideBearing value="37"/>
+    <minRightSideBearing value="19"/>
+    <xMaxExtent value="640"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="32"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <maxTwilightPoints value="16"/>
+    <maxStorage value="47"/>
+    <maxFunctionDefs value="66"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="1036"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="1"/>
+    <xAvgCharWidth value="554"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="717"/>
+    <ySubscriptYSize value="666"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="142"/>
+    <ySuperscriptXSize value="717"/>
+    <ySuperscriptYSize value="666"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="488"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="265"/>
+    <sFamilyClass value="2053"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="6"/>
+      <bProportion value="4"/>
+      <bContrast value="2"/>
+      <bStrokeVariation value="2"/>
+      <bArmStyle value="2"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="Mono"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="99"/>
+    <sTypoAscender value="746"/>
+    <sTypoDescender value="-216"/>
+    <sTypoLineGap value="154"/>
+    <usWinAscent value="927"/>
+    <usWinDescent value="217"/>
+    <ulCodePageRange1 value="01000000 00000000 00000001 11111111"/>
+    <ulCodePageRange2 value="11111111 11111111 00000000 00000000"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="768" lsb="128"/>
+    <mtx name="a" width="569" lsb="37"/>
+    <mtx name="b" width="569" lsb="67"/>
+    <mtx name="c" width="512" lsb="40"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
+      <map code="0x63" name="c"/><!-- LATIN SMALL LETTER C -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="128" yMin="0" xMax="640" yMax="640">
+      <contour>
+        <pt x="128" y="0" on="1"/>
+        <pt x="128" y="640" on="1"/>
+        <pt x="640" y="640" on="1"/>
+        <pt x="640" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="144" y="16" on="1"/>
+        <pt x="624" y="16" on="1"/>
+        <pt x="624" y="624" on="1"/>
+        <pt x="144" y="624" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="37" yMin="0" xMax="526" yMax="543">
+      <contour>
+        <pt x="414" y="66" on="1"/>
+        <pt x="337" y="0" on="0"/>
+        <pt x="217" y="0" on="1"/>
+        <pt x="37" y="0" on="0"/>
+        <pt x="37" y="140" on="1"/>
+        <pt x="37" y="281" on="0"/>
+        <pt x="246" y="306" on="1"/>
+        <pt x="354" y="319" on="0"/>
+        <pt x="406" y="337" on="1"/>
+        <pt x="406" y="468" on="0"/>
+        <pt x="279" y="468" on="1"/>
+        <pt x="166" y="468" on="0"/>
+        <pt x="144" y="380" on="1"/>
+        <pt x="52" y="380" on="1"/>
+        <pt x="86" y="543" on="0"/>
+        <pt x="498" y="543" on="0"/>
+        <pt x="498" y="342" on="1"/>
+        <pt x="498" y="222" on="1"/>
+        <pt x="498" y="96" on="0"/>
+        <pt x="509" y="30" on="0"/>
+        <pt x="526" y="0" on="1"/>
+        <pt x="432" y="0" on="1"/>
+        <pt x="418" y="28" on="0"/>
+      </contour>
+      <contour>
+        <pt x="406" y="266" on="1"/>
+        <pt x="360" y="246" on="0"/>
+        <pt x="260" y="232" on="1"/>
+        <pt x="129" y="214" on="0"/>
+        <pt x="129" y="144" on="1"/>
+        <pt x="129" y="78" on="0"/>
+        <pt x="239" y="78" on="1"/>
+        <pt x="406" y="78" on="0"/>
+        <pt x="406" y="234" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="67" yMin="0" xMax="528" yMax="733">
+      <contour>
+        <pt x="157" y="0" on="1"/>
+        <pt x="67" y="0" on="1"/>
+        <pt x="67" y="733" on="1"/>
+        <pt x="157" y="733" on="1"/>
+        <pt x="157" y="472" on="1"/>
+        <pt x="214" y="543" on="0"/>
+        <pt x="302" y="543" on="1"/>
+        <pt x="528" y="543" on="0"/>
+        <pt x="528" y="274" on="1"/>
+        <pt x="528" y="0" on="0"/>
+        <pt x="157" y="0" on="0"/>
+        <pt x="157" y="66" on="1"/>
+      </contour>
+      <contour>
+        <pt x="157" y="270" on="1"/>
+        <pt x="157" y="76" on="0"/>
+        <pt x="297" y="76" on="1"/>
+        <pt x="442" y="76" on="0"/>
+        <pt x="442" y="266" on="1"/>
+        <pt x="442" y="469" on="0"/>
+        <pt x="294" y="469" on="1"/>
+        <pt x="157" y="469" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="c" xMin="40" yMin="0" xMax="493" yMax="543">
+      <contour>
+        <pt x="400" y="182" on="1"/>
+        <pt x="493" y="182" on="1"/>
+        <pt x="464" y="0" on="0"/>
+        <pt x="281" y="0" on="1"/>
+        <pt x="40" y="0" on="0"/>
+        <pt x="40" y="264" on="1"/>
+        <pt x="40" y="543" on="0"/>
+        <pt x="282" y="543" on="1"/>
+        <pt x="460" y="543" on="0"/>
+        <pt x="493" y="375" on="1"/>
+        <pt x="400" y="375" on="1"/>
+        <pt x="378" y="469" on="0"/>
+        <pt x="286" y="469" on="1"/>
+        <pt x="132" y="469" on="0"/>
+        <pt x="132" y="266" on="1"/>
+        <pt x="132" y="78" on="0"/>
+        <pt x="279" y="78" on="1"/>
+        <pt x="384" y="78" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      abc
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      ABC-Skia-Test-Font
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      abc
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      abcNormal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-108"/>
+    <underlineThickness value="75"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/resources/particles/orientation.json b/resources/particles/orientation.json
index 885038d..87d629b 100644
--- a/resources/particles/orientation.json
+++ b/resources/particles/orientation.json
@@ -2,7 +2,8 @@
    "MaxCount": 32,
    "Drawable": {
       "Type": "SkImageDrawable",
-      "Path": "resources/images/index8.png",
+      "Path": "images",
+      "Name": "index8.png",
       "Columns": 1,
       "Rows": 1
    },
diff --git a/resources/particles/sprite_frame.json b/resources/particles/sprite_frame.json
index 645d2b6..70e56af 100644
--- a/resources/particles/sprite_frame.json
+++ b/resources/particles/sprite_frame.json
@@ -2,7 +2,8 @@
    "MaxCount": 32,
    "Drawable": {
       "Type": "SkImageDrawable",
-      "Path": "resources/images/explosion_sprites.png",
+      "Path": "images",
+      "Name": "explosion_sprites.png",
       "Columns": 4,
       "Rows": 4
    },
diff --git a/resources/skottie/skottie-3d-2planes.json b/resources/skottie/skottie-3d-2planes.json
new file mode 100644
index 0000000..997ee23
--- /dev/null
+++ b/resources/skottie/skottie-3d-2planes.json
@@ -0,0 +1,990 @@
+{
+  "assets": [
+    {
+      "id": "comp_0",
+      "layers": [
+        {
+          "ao": 0,
+          "bm": 0,
+          "ddd": 0,
+          "ind": 1,
+          "ip": 0,
+          "ks": {
+            "a": {
+              "a": 0,
+              "ix": 1,
+              "k": [
+                0,
+                0,
+                0
+              ]
+            },
+            "o": {
+              "a": 0,
+              "ix": 11,
+              "k": 35
+            },
+            "p": {
+              "a": 0,
+              "ix": 2,
+              "k": [
+                50,
+                50,
+                0
+              ]
+            },
+            "r": {
+              "a": 0,
+              "ix": 10,
+              "k": 0
+            },
+            "s": {
+              "a": 0,
+              "ix": 6,
+              "k": [
+                100,
+                100,
+                100
+              ]
+            }
+          },
+          "nm": "Shape Layer 1",
+          "op": 1203,
+          "shapes": [
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 6",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      -20,
+                      -20
+                    ],
+                    "t": 450,
+                    "ti": [
+                      0,
+                      -3.333
+                    ],
+                    "to": [
+                      0,
+                      3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      -20,
+                      0
+                    ],
+                    "t": 570
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 4",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      -20
+                    ],
+                    "t": 600,
+                    "ti": [
+                      3.333,
+                      0
+                    ],
+                    "to": [
+                      -3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      -20,
+                      -20
+                    ],
+                    "t": 720
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 5",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      -20
+                    ],
+                    "t": 750,
+                    "ti": [
+                      3.333,
+                      0
+                    ],
+                    "to": [
+                      -3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      -20
+                    ],
+                    "t": 870
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 3",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      -20,
+                      0
+                    ],
+                    "t": 300,
+                    "ti": [
+                      -3.333,
+                      0
+                    ],
+                    "to": [
+                      3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      0
+                    ],
+                    "t": 420
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 1",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      0
+                    ],
+                    "t": 150,
+                    "ti": [
+                      0,
+                      -3.333
+                    ],
+                    "to": [
+                      0,
+                      3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      20
+                    ],
+                    "t": 270
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 2",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      0
+                    ],
+                    "t": 900,
+                    "ti": [
+                      0,
+                      3.333
+                    ],
+                    "to": [
+                      0,
+                      -3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      20,
+                      -20
+                    ],
+                    "t": 1020
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 8",
+              "p": {
+                "a": 0,
+                "ix": 3,
+                "k": [
+                  -20,
+                  20
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 7",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      20
+                    ],
+                    "t": 0,
+                    "ti": [
+                      -3.333,
+                      0
+                    ],
+                    "to": [
+                      3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      20
+                    ],
+                    "t": 120,
+                    "ti": [
+                      0,
+                      0
+                    ],
+                    "to": [
+                      0,
+                      0
+                    ]
+                  },
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      20
+                    ],
+                    "t": 1050,
+                    "ti": [
+                      0,
+                      3.333
+                    ],
+                    "to": [
+                      0,
+                      -3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      20,
+                      0
+                    ],
+                    "t": 1170
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "bm": 0,
+              "c": {
+                "a": 0,
+                "ix": 4,
+                "k": [
+                  0,
+                  1,
+                  0.263311892748,
+                  1
+                ]
+              },
+              "hd": false,
+              "mn": "ADBE Vector Graphic - Fill",
+              "nm": "Fill 1",
+              "o": {
+                "a": 0,
+                "ix": 5,
+                "k": 100
+              },
+              "r": 1,
+              "ty": "fl"
+            }
+          ],
+          "sr": 1,
+          "st": 0,
+          "ty": 4
+        }
+      ]
+    }
+  ],
+  "ddd": 1,
+  "fr": 60,
+  "h": 100,
+  "ip": 0,
+  "layers": [
+    {
+      "bm": 0,
+      "ddd": 0,
+      "ind": 1,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 1,
+          "ix": 2,
+          "k": [
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                50,
+                50,
+                -200
+              ],
+              "t": 0,
+              "ti": [
+                0,
+                0,
+                0
+              ],
+              "to": [
+                -25,
+                41.667,
+                0
+              ]
+            },
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                -100,
+                300,
+                -200
+              ],
+              "t": 360,
+              "ti": [
+                -41.21,
+                68.683,
+                0
+              ],
+              "to": [
+                0,
+                0,
+                0
+              ]
+            },
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                200,
+                300,
+                -200
+              ],
+              "t": 840,
+              "ti": [
+                -10.083,
+                16.805,
+                0
+              ],
+              "to": [
+                27.854,
+                -46.424,
+                0
+              ]
+            },
+            {
+              "s": [
+                50,
+                50,
+                -200
+              ],
+              "t": 1200
+            }
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 1,
+          "ix": 10,
+          "k": [
+            {
+              "i": {
+                "x": [
+                  0.833
+                ],
+                "y": [
+                  0.833
+                ]
+              },
+              "o": {
+                "x": [
+                  0.167
+                ],
+                "y": [
+                  0.167
+                ]
+              },
+              "s": [
+                0
+              ],
+              "t": 0
+            },
+            {
+              "s": [
+                360
+              ],
+              "t": 1200
+            }
+          ]
+        }
+      },
+      "nm": "Camera 1",
+      "op": 1201,
+      "pe": {
+        "a": 0,
+        "ix": 1,
+        "k": 269
+      },
+      "sr": 1,
+      "st": 0,
+      "ty": 13
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 1,
+      "h": 100,
+      "ind": 2,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            20
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "bottom",
+      "op": 1201,
+      "refId": "comp_0",
+      "sr": 1,
+      "st": 0,
+      "tm": {
+        "a": 1,
+        "ix": 2,
+        "k": [
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              20
+            ],
+            "t": 0
+          },
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              0
+            ],
+            "t": 1200
+          },
+          {
+            "s": [
+              20.017
+            ],
+            "t": 1201
+          }
+        ]
+      },
+      "ty": 0,
+      "w": 100
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 1,
+      "h": 100,
+      "ind": 4,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            -20
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "top",
+      "op": 1201,
+      "refId": "comp_0",
+      "sr": 1,
+      "st": 0,
+      "ty": 0,
+      "w": 100
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 5,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Black Solid 1",
+      "op": 1201,
+      "sc": "#000000",
+      "sh": 100,
+      "sr": 1,
+      "st": 0,
+      "sw": 100,
+      "ty": 1
+    }
+  ],
+  "markers": [],
+  "nm": "cube",
+  "op": 1201,
+  "v": "5.5.5",
+  "w": 100
+}
\ No newline at end of file
diff --git a/resources/skottie/skottie-3d-3planes.json b/resources/skottie/skottie-3d-3planes.json
new file mode 100644
index 0000000..733eb22
--- /dev/null
+++ b/resources/skottie/skottie-3d-3planes.json
@@ -0,0 +1,1141 @@
+{
+  "assets": [
+    {
+      "id": "comp_0",
+      "layers": [
+        {
+          "ao": 0,
+          "bm": 0,
+          "ddd": 0,
+          "ind": 1,
+          "ip": 0,
+          "ks": {
+            "a": {
+              "a": 0,
+              "ix": 1,
+              "k": [
+                0,
+                0,
+                0
+              ]
+            },
+            "o": {
+              "a": 0,
+              "ix": 11,
+              "k": 35
+            },
+            "p": {
+              "a": 0,
+              "ix": 2,
+              "k": [
+                50,
+                50,
+                0
+              ]
+            },
+            "r": {
+              "a": 0,
+              "ix": 10,
+              "k": 0
+            },
+            "s": {
+              "a": 0,
+              "ix": 6,
+              "k": [
+                100,
+                100,
+                100
+              ]
+            }
+          },
+          "nm": "Shape Layer 1",
+          "op": 1203,
+          "shapes": [
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 6",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      -20,
+                      -20
+                    ],
+                    "t": 450,
+                    "ti": [
+                      0,
+                      -3.333
+                    ],
+                    "to": [
+                      0,
+                      3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      -20,
+                      0
+                    ],
+                    "t": 570
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 4",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      -20
+                    ],
+                    "t": 600,
+                    "ti": [
+                      3.333,
+                      0
+                    ],
+                    "to": [
+                      -3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      -20,
+                      -20
+                    ],
+                    "t": 720
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 5",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      -20
+                    ],
+                    "t": 750,
+                    "ti": [
+                      3.333,
+                      0
+                    ],
+                    "to": [
+                      -3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      -20
+                    ],
+                    "t": 870
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 3",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      -20,
+                      0
+                    ],
+                    "t": 300,
+                    "ti": [
+                      -3.333,
+                      0
+                    ],
+                    "to": [
+                      3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      0
+                    ],
+                    "t": 420
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 1",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      0
+                    ],
+                    "t": 150,
+                    "ti": [
+                      0,
+                      -3.333
+                    ],
+                    "to": [
+                      0,
+                      3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      0,
+                      20
+                    ],
+                    "t": 270
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 2",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      0
+                    ],
+                    "t": 900,
+                    "ti": [
+                      0,
+                      3.333
+                    ],
+                    "to": [
+                      0,
+                      -3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      20,
+                      -20
+                    ],
+                    "t": 1020
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 8",
+              "p": {
+                "a": 0,
+                "ix": 3,
+                "k": [
+                  -20,
+                  20
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "d": 1,
+              "hd": false,
+              "mn": "ADBE Vector Shape - Rect",
+              "nm": "Rectangle Path 7",
+              "p": {
+                "a": 1,
+                "ix": 3,
+                "k": [
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      0,
+                      20
+                    ],
+                    "t": 0,
+                    "ti": [
+                      -3.333,
+                      0
+                    ],
+                    "to": [
+                      3.333,
+                      0
+                    ]
+                  },
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      20
+                    ],
+                    "t": 120,
+                    "ti": [
+                      0,
+                      0
+                    ],
+                    "to": [
+                      0,
+                      0
+                    ]
+                  },
+                  {
+                    "i": {
+                      "x": 0.833,
+                      "y": 0.833
+                    },
+                    "o": {
+                      "x": 0.167,
+                      "y": 0.167
+                    },
+                    "s": [
+                      20,
+                      20
+                    ],
+                    "t": 1050,
+                    "ti": [
+                      0,
+                      3.333
+                    ],
+                    "to": [
+                      0,
+                      -3.333
+                    ]
+                  },
+                  {
+                    "s": [
+                      20,
+                      0
+                    ],
+                    "t": 1170
+                  }
+                ]
+              },
+              "r": {
+                "a": 0,
+                "ix": 4,
+                "k": 0
+              },
+              "s": {
+                "a": 0,
+                "ix": 2,
+                "k": [
+                  16,
+                  16
+                ]
+              },
+              "ty": "rc"
+            },
+            {
+              "bm": 0,
+              "c": {
+                "a": 0,
+                "ix": 4,
+                "k": [
+                  0,
+                  1,
+                  0.263311892748,
+                  1
+                ]
+              },
+              "hd": false,
+              "mn": "ADBE Vector Graphic - Fill",
+              "nm": "Fill 1",
+              "o": {
+                "a": 0,
+                "ix": 5,
+                "k": 100
+              },
+              "r": 1,
+              "ty": "fl"
+            }
+          ],
+          "sr": 1,
+          "st": 0,
+          "ty": 4
+        }
+      ]
+    }
+  ],
+  "ddd": 1,
+  "fr": 60,
+  "h": 100,
+  "ip": 0,
+  "layers": [
+    {
+      "bm": 0,
+      "ddd": 0,
+      "ind": 1,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 1,
+          "ix": 2,
+          "k": [
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                50,
+                50,
+                -200
+              ],
+              "t": 0,
+              "ti": [
+                0,
+                0,
+                0
+              ],
+              "to": [
+                -25,
+                41.667,
+                0
+              ]
+            },
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                -100,
+                300,
+                -200
+              ],
+              "t": 360,
+              "ti": [
+                -41.21,
+                68.683,
+                0
+              ],
+              "to": [
+                0,
+                0,
+                0
+              ]
+            },
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "s": [
+                200,
+                300,
+                -200
+              ],
+              "t": 840,
+              "ti": [
+                -10.083,
+                16.805,
+                0
+              ],
+              "to": [
+                27.854,
+                -46.424,
+                0
+              ]
+            },
+            {
+              "s": [
+                50,
+                50,
+                -200
+              ],
+              "t": 1200
+            }
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 1,
+          "ix": 10,
+          "k": [
+            {
+              "i": {
+                "x": [
+                  0.833
+                ],
+                "y": [
+                  0.833
+                ]
+              },
+              "o": {
+                "x": [
+                  0.167
+                ],
+                "y": [
+                  0.167
+                ]
+              },
+              "s": [
+                0
+              ],
+              "t": 0
+            },
+            {
+              "s": [
+                360
+              ],
+              "t": 1200
+            }
+          ]
+        }
+      },
+      "nm": "Camera 1",
+      "op": 1201,
+      "pe": {
+        "a": 0,
+        "ix": 1,
+        "k": 269
+      },
+      "sr": 1,
+      "st": 0,
+      "ty": 13
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 1,
+      "h": 100,
+      "ind": 2,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            20
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "bottom",
+      "op": 1201,
+      "refId": "comp_0",
+      "sr": 1,
+      "st": 0,
+      "tm": {
+        "a": 1,
+        "ix": 2,
+        "k": [
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              20
+            ],
+            "t": 0
+          },
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              0
+            ],
+            "t": 1200
+          },
+          {
+            "s": [
+              20.017
+            ],
+            "t": 1201
+          }
+        ]
+      },
+      "ty": 0,
+      "w": 100
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 1,
+      "h": 100,
+      "ind": 3,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "mid",
+      "op": 1201,
+      "refId": "comp_0",
+      "sr": 1,
+      "st": 0,
+      "tm": {
+        "a": 1,
+        "ix": 2,
+        "k": [
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              5
+            ],
+            "t": 0
+          },
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              15
+            ],
+            "t": 600
+          },
+          {
+            "i": {
+              "x": [
+                0.833
+              ],
+              "y": [
+                0.833
+              ]
+            },
+            "o": {
+              "x": [
+                0.167
+              ],
+              "y": [
+                0.167
+              ]
+            },
+            "s": [
+              5
+            ],
+            "t": 1200
+          },
+          {
+            "s": [
+              20.017
+            ],
+            "t": 1201
+          }
+        ]
+      },
+      "ty": 0,
+      "w": 100
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 1,
+      "h": 100,
+      "ind": 4,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "or": {
+          "a": 0,
+          "ix": 7,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            -20
+          ]
+        },
+        "rx": {
+          "a": 0,
+          "ix": 8,
+          "k": 0
+        },
+        "ry": {
+          "a": 0,
+          "ix": 9,
+          "k": 0
+        },
+        "rz": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "top",
+      "op": 1201,
+      "refId": "comp_0",
+      "sr": 1,
+      "st": 0,
+      "ty": 0,
+      "w": 100
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 5,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            50,
+            50,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Black Solid 1",
+      "op": 1201,
+      "sc": "#000000",
+      "sh": 100,
+      "sr": 1,
+      "st": 0,
+      "sw": 100,
+      "ty": 1
+    }
+  ],
+  "markers": [],
+  "nm": "cube",
+  "op": 1201,
+  "v": "5.5.5",
+  "w": 100
+}
\ No newline at end of file
diff --git a/resources/skottie/skottie-3d-parenting-camera.json b/resources/skottie/skottie-3d-parenting-camera.json
new file mode 100644
index 0000000..094e574
--- /dev/null
+++ b/resources/skottie/skottie-3d-parenting-camera.json
@@ -0,0 +1 @@
+{"v":"5.5.5","fr":60,"ip":0,"op":301,"w":500,"h":500,"nm":"3d parenting","ddd":1,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":9,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":50,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":15,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.872441768646,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":300,"s":[360]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.480912983418,0.342815577984,0.959742665291,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.581096827984,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":301,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":13,"nm":"Camera 1","sr":1,"pe":{"a":0,"k":1066.667,"ix":1},"ks":{"a":{"a":0,"k":[250,250,0],"ix":1},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[250,250,-1066.667],"to":[-41.667,83.333,61.111],"ti":[-41.667,-83.333,-61.111]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[0,750,-700],"to":[41.667,83.333,61.111],"ti":[-41.667,83.333,61.111]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":210,"s":[500,750,-700],"to":[41.667,-83.333,-61.111],"ti":[41.667,83.333,61.111]},{"t":300,"s":[250,250,-1066.667]}],"ix":2},"or":{"a":0,"k":[0,0,0],"ix":7},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10}},"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"2d -> no parent","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,150,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":3,"nm":"3d null","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"2d -> 3d null","parent":3,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-100,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"2d -> 3d layer","parent":6,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[550,50,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":6,"ty":0,"nm":"3d -> no parent","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[100,350,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"2d null","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":8,"ty":0,"nm":"3d -> 2d null","parent":7,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[0,100,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":9,"ty":0,"nm":"3d -> 2d layer","parent":2,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[550,450,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":1,"nm":"Light Gray Solid 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#c8c8c8","ip":0,"op":301,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-3d-parenting-nocamera.json b/resources/skottie/skottie-3d-parenting-nocamera.json
new file mode 100644
index 0000000..3da4dbc
--- /dev/null
+++ b/resources/skottie/skottie-3d-parenting-nocamera.json
@@ -0,0 +1 @@
+{"v":"5.5.5","fr":60,"ip":0,"op":301,"w":500,"h":500,"nm":"3d parenting","ddd":1,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":9,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":50,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":15,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.872441768646,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":300,"s":[360]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.480912983418,0.342815577984,0.959742665291,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.581096827984,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":301,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"2d -> no parent","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,150,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"3d null","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"2d -> 3d null","parent":2,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-100,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"2d -> 3d layer","parent":5,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[550,50,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":5,"ty":0,"nm":"3d -> no parent","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[100,350,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"nm":"2d null","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":7,"ty":0,"nm":"3d -> 2d null","parent":6,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[0,100,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":1,"ind":8,"ty":0,"nm":"3d -> 2d layer","parent":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[550,450,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":500,"h":500,"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":1,"nm":"Light Gray Solid 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#c8c8c8","ip":0,"op":301,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-camera-one-node.json b/resources/skottie/skottie-camera-one-node.json
new file mode 100644
index 0000000..19cbb5c
--- /dev/null
+++ b/resources/skottie/skottie-camera-one-node.json
@@ -0,0 +1 @@
+{"v":"5.5.5","fr":60,"ip":0,"op":601,"w":1920,"h":1080,"nm":"Comp 1","ddd":1,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[1500,750],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[812,403],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.171292886138,0.169715076685,0.803477346897,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":13,"nm":"Camera 1","sr":1,"pe":{"a":0,"k":2133.333,"ix":1},"ks":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[560,740,-2133.333],"to":[133.333,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[1360,740,-2133.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":120,"s":[1360,340,-2133.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":180,"s":[560,340,-2133.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":240,"s":[560,740,-2133.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":300,"s":[560,740,-3104.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":360,"s":[1360,740,-3104.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":420,"s":[1360,340,-3104.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":480,"s":[560,340,-3104.333],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":540,"s":[560,740,-3104.333],"to":[0,0,0],"ti":[0,0,0]},{"t":600,"s":[560,740,-2133.3]}],"ix":2},"or":{"a":0,"k":[0,0,0],"ix":7},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10}},"ip":0,"op":601,"st":0,"bm":0},{"ddd":1,"ind":3,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,1000],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[35,35],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":603,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":1,"nm":"Light Gray Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[960,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":1920,"sh":1080,"sc":"#c8c8c8","ip":0,"op":603,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-gradient-opacity.json b/resources/skottie/skottie-gradient-opacity.json
new file mode 100644
index 0000000..db0057b
--- /dev/null
+++ b/resources/skottie/skottie-gradient-opacity.json
@@ -0,0 +1,585 @@
+{
+  "assets": [],
+  "ddd": 0,
+  "fr": 60,
+  "h": 500,
+  "ip": 0,
+  "layers": [
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 1,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            250,
+            75,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Shape Layer 1",
+      "op": 301,
+      "shapes": [
+        {
+          "d": 1,
+          "hd": false,
+          "mn": "ADBE Vector Shape - Rect",
+          "nm": "Rectangle Path 1",
+          "p": {
+            "a": 0,
+            "ix": 3,
+            "k": [
+              0,
+              0
+            ]
+          },
+          "r": {
+            "a": 0,
+            "ix": 4,
+            "k": 0
+          },
+          "s": {
+            "a": 0,
+            "ix": 2,
+            "k": [
+              400,
+              100
+            ]
+          },
+          "ty": "rc"
+        },
+        {
+          "bm": 0,
+          "e": {
+            "a": 0,
+            "ix": 6,
+            "k": [
+              200,
+              0
+            ]
+          },
+          "g": {
+            "k": {
+              "a": 0,
+              "ix": 9,
+              "k": [
+                0.50, 0, 1, 0,
+                0.50, 1, 0, 0,
+                0.00, 1,
+                0.25, 0,
+                0.50, 1,
+                0.75, 0,
+                1.00, 1
+              ]
+            },
+            "p": 2
+          },
+          "hd": false,
+          "mn": "ADBE Vector Graphic - G-Fill",
+          "nm": "Gradient Fill 1",
+          "o": {
+            "a": 0,
+            "ix": 10,
+            "k": 100
+          },
+          "r": 1,
+          "s": {
+            "a": 0,
+            "ix": 5,
+            "k": [
+              -200,
+              0
+            ]
+          },
+          "t": 1,
+          "ty": "gf"
+        }
+      ],
+      "sr": 1,
+      "st": 0,
+      "ty": 4
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 2,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            250,
+            190,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Shape Layer 2",
+      "op": 301,
+      "shapes": [
+        {
+          "d": 1,
+          "hd": false,
+          "mn": "ADBE Vector Shape - Rect",
+          "nm": "Rectangle Path 1",
+          "p": {
+            "a": 0,
+            "ix": 3,
+            "k": [
+              0,
+              0
+            ]
+          },
+          "r": {
+            "a": 0,
+            "ix": 4,
+            "k": 0
+          },
+          "s": {
+            "a": 0,
+            "ix": 2,
+            "k": [
+              400,
+              100
+            ]
+          },
+          "ty": "rc"
+        },
+        {
+          "bm": 0,
+          "e": {
+            "a": 0,
+            "ix": 6,
+            "k": [
+              200,
+              0
+            ]
+          },
+          "g": {
+            "k": {
+              "a": 0,
+              "ix": 9,
+              "k": [
+                0.00, 0, 1, 0,
+                0.25, 1, 0, 0,
+                0.50, 0, 0, 1,
+                0.75, 1, 0, 0,
+                1.00, 0, 1, 0,
+
+                0.50, 0.35,
+                0.50, 1.00
+              ]
+            },
+            "p": 5
+          },
+          "hd": false,
+          "mn": "ADBE Vector Graphic - G-Fill",
+          "nm": "Gradient Fill 1",
+          "o": {
+            "a": 0,
+            "ix": 10,
+            "k": 100
+          },
+          "r": 1,
+          "s": {
+            "a": 0,
+            "ix": 5,
+            "k": [
+              -200,
+              0
+            ]
+          },
+          "t": 1,
+          "ty": "gf"
+        }
+      ],
+      "sr": 1,
+      "st": 0,
+      "ty": 4
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 3,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            250,
+            305,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Shape Layer 3",
+      "op": 301,
+      "shapes": [
+        {
+          "d": 1,
+          "hd": false,
+          "mn": "ADBE Vector Shape - Rect",
+          "nm": "Rectangle Path 1",
+          "p": {
+            "a": 0,
+            "ix": 3,
+            "k": [
+              0,
+              0
+            ]
+          },
+          "r": {
+            "a": 0,
+            "ix": 4,
+            "k": 0
+          },
+          "s": {
+            "a": 0,
+            "ix": 2,
+            "k": [
+              400,
+              100
+            ]
+          },
+          "ty": "rc"
+        },
+        {
+          "bm": 0,
+          "e": {
+            "a": 0,
+            "ix": 6,
+            "k": [
+              200,
+              0
+            ]
+          },
+          "g": {
+            "k": {
+              "a": 0,
+              "ix": 9,
+              "k": [
+                0.00, 0, 1, 0,
+                0.25, 1, 0, 0,
+                0.50, 0, 0, 1,
+                0.75, 1, 0, 0,
+                1.00, 0, 1, 0,
+
+                0.25, 1,
+                0.37, 0,
+                0.50, 1,
+                0.62, 0,
+                0.75, 1
+              ]
+            },
+            "p": 5
+          },
+          "hd": false,
+          "mn": "ADBE Vector Graphic - G-Fill",
+          "nm": "Gradient Fill 1",
+          "o": {
+            "a": 0,
+            "ix": 10,
+            "k": 100
+          },
+          "r": 1,
+          "s": {
+            "a": 0,
+            "ix": 5,
+            "k": [
+              -200,
+              0
+            ]
+          },
+          "t": 1,
+          "ty": "gf"
+        }
+      ],
+      "sr": 1,
+      "st": 0,
+      "ty": 4
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 4,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            0,
+            0,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            250,
+            425,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "Shape Layer 4",
+      "op": 301,
+      "shapes": [
+        {
+          "d": 1,
+          "hd": false,
+          "mn": "ADBE Vector Shape - Rect",
+          "nm": "Rectangle Path 1",
+          "p": {
+            "a": 0,
+            "ix": 3,
+            "k": [
+              0,
+              0
+            ]
+          },
+          "r": {
+            "a": 0,
+            "ix": 4,
+            "k": 0
+          },
+          "s": {
+            "a": 0,
+            "ix": 2,
+            "k": [
+              400,
+              100
+            ]
+          },
+          "ty": "rc"
+        },
+        {
+          "bm": 0,
+          "e": {
+            "a": 0,
+            "ix": 6,
+            "k": [
+              200,
+              0
+            ]
+          },
+          "g": {
+            "k": {
+              "a": 0,
+              "ix": 9,
+              "k": [
+                0.25, 0, 1, 0,
+                0.37, 1, 0, 0,
+                0.50, 0, 0, 1,
+                0.62, 1, 0, 0,
+                0.75, 0, 1, 0,
+
+                0.00, 1,
+                0.25, 0,
+                0.50, 1,
+                0.75, 0,
+                1.00, 1
+              ]
+            },
+            "p": 5
+          },
+          "hd": false,
+          "mn": "ADBE Vector Graphic - G-Fill",
+          "nm": "Gradient Fill 1",
+          "o": {
+            "a": 0,
+            "ix": 10,
+            "k": 100
+          },
+          "r": 1,
+          "s": {
+            "a": 0,
+            "ix": 5,
+            "k": [
+              -200,
+              0
+            ]
+          },
+          "t": 1,
+          "ty": "gf"
+        }
+      ],
+      "sr": 1,
+      "st": 0,
+      "ty": 4
+    },
+    {
+      "ao": 0,
+      "bm": 0,
+      "ddd": 0,
+      "ind": 5,
+      "ip": 0,
+      "ks": {
+        "a": {
+          "a": 0,
+          "ix": 1,
+          "k": [
+            250,
+            250,
+            0
+          ]
+        },
+        "o": {
+          "a": 0,
+          "ix": 11,
+          "k": 100
+        },
+        "p": {
+          "a": 0,
+          "ix": 2,
+          "k": [
+            250,
+            250,
+            0
+          ]
+        },
+        "r": {
+          "a": 0,
+          "ix": 10,
+          "k": 0
+        },
+        "s": {
+          "a": 0,
+          "ix": 6,
+          "k": [
+            100,
+            100,
+            100
+          ]
+        }
+      },
+      "nm": "White Solid 1",
+      "op": 301,
+      "sc": "#ffffff",
+      "sh": 500,
+      "sr": 1,
+      "st": 0,
+      "sw": 500,
+      "ty": 1
+    }
+  ],
+  "markers": [],
+  "nm": "gradient opacity",
+  "op": 301,
+  "v": "5.5.5",
+  "w": 500
+}
diff --git a/resources/skottie/skottie-matte-blendmode.json b/resources/skottie/skottie-matte-blendmode.json
new file mode 100644
index 0000000..431fc7a
--- /dev/null
+++ b/resources/skottie/skottie-matte-blendmode.json
@@ -0,0 +1 @@
+{"v":"5.5.5","fr":60,"ip":0,"op":301,"w":500,"h":500,"nm":"matted-blendmode","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"mask","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":6,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":50,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":162,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":300,"s":[120]}],"ix":6},"o":{"a":0,"k":75,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[200,200],"ix":2},"p":{"a":0,"k":[100,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":300,"s":[-360]}],"ix":6},"o":{"a":0,"k":75,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Red Solid 1","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#ff0000","ip":0,"op":301,"st":0,"bm":1},{"ddd":0,"ind":3,"ty":1,"nm":"Green Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#00ff00","ip":0,"op":301,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-trimpath-modes.json b/resources/skottie/skottie-trimpath-modes.json
new file mode 100644
index 0000000..2a98463
--- /dev/null
+++ b/resources/skottie/skottie-trimpath-modes.json
@@ -0,0 +1 @@
+{"v":"5.5.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"trim path","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,125,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[400,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"d":1,"ty":"el","s":{"a":0,"k":[350,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":26,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":54,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":3,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":600,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":4,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.400428920984,0.433624386787,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,375,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[400,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"d":1,"ty":"el","s":{"a":0,"k":[350,150],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":26,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":54,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":3,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":600,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":2,"ix":4,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.400428920984,0.433624386787,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"White Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#ffffff","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/samplecode/SampleAndroidShadows.cpp b/samplecode/SampleAndroidShadows.cpp
index 71eb22a..a4518f2 100644
--- a/samplecode/SampleAndroidShadows.cpp
+++ b/samplecode/SampleAndroidShadows.cpp
@@ -53,7 +53,7 @@
         fRRPath.addRRect(SkRRect::MakeRectXY(SkRect::MakeXYWH(-100, -50, 200, 100), 4, 4));
         fFunkyRRPath.addRoundRect(SkRect::MakeXYWH(-50, -50, SK_Scalar1 * 100, SK_Scalar1 * 100),
                                   40 * SK_Scalar1, 20 * SK_Scalar1,
-                                  SkPath::kCW_Direction);
+                                  SkPathDirection::kCW);
         fCubicPath.cubicTo(100 * SK_Scalar1, 50 * SK_Scalar1,
                            20 * SK_Scalar1, 100 * SK_Scalar1,
                            0 * SK_Scalar1, 0 * SK_Scalar1);
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index 2f70ff2..dff8ed9 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -28,7 +28,7 @@
 #include "src/gpu/ccpr/GrVSCoverageProcessor.h"
 #include "src/gpu/geometry/GrPathUtils.h"
 #include "src/gpu/gl/GrGLGpu.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/ops/GrDrawOp.h"
 
 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
@@ -196,7 +196,7 @@
         GrPaint paint;
         paint.addColorFragmentProcessor(
                 GrSimpleTextureEffect::Make(sk_ref_sp(ccbuff->asTextureProxy()),
-                                            ccbuff->colorInfo().colorType(), SkMatrix::I()));
+                                            ccbuff->colorInfo().alphaType(), SkMatrix::I()));
         paint.addColorFragmentProcessor(
                 skstd::make_unique<VisualizeCoverageCountFP>());
         paint.setPorterDuffXPFactory(SkBlendMode::kSrcOver);
diff --git a/samplecode/SampleClip.cpp b/samplecode/SampleClip.cpp
index e931881..0e2546e 100644
--- a/samplecode/SampleClip.cpp
+++ b/samplecode/SampleClip.cpp
@@ -135,3 +135,380 @@
 };
 
 DEF_SAMPLE( return new ClipView(); )
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkHalfPlane {
+    SkScalar fA, fB, fC;
+
+    SkScalar operator()(SkScalar x, SkScalar y) const {
+        return fA * x + fB * y + fC;
+    }
+
+    bool twoPts(SkPoint pts[2]) const {
+        if (fB) {
+            pts[0] = { 0, -fC / fB };
+            pts[1] = { 1, (-fC - fA) / fB };
+        } else if (fA) {
+            pts[0] = { -fC / fA,        0 };
+            pts[1] = { (-fC - fB) / fA, 1 };
+        } else {
+            return false;
+        }
+        return true;
+    }
+};
+
+#include "src/core/SkEdgeClipper.h"
+
+static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
+    SkMatrix mx, inv;
+    SkVector v = p1 - p0;
+    mx.setAll(v.fX, -v.fY, p0.fX,
+              v.fY,  v.fX, p0.fY,
+                 0,     0,     1);
+    SkAssertResult(mx.invert(&inv));
+
+    SkPath rotated;
+    path.transform(inv, &rotated);
+
+    SkScalar big = 1e28f;
+    SkRect clip = {-big, 0, big, big };
+
+    struct Rec {
+        SkPath  fResult;
+        SkPoint fPrev;
+    } rec;
+
+    SkEdgeClipper::ClipPath(rotated, clip, false,
+                            [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
+        Rec* rec = (Rec*)ctx;
+
+        bool addLineTo = false;
+        SkPoint      pts[4];
+        SkPath::Verb verb;
+        while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
+            if (newCtr) {
+                rec->fResult.moveTo(pts[0]);
+                rec->fPrev = pts[0];
+                newCtr = false;
+            }
+
+            if (addLineTo || pts[0] != rec->fPrev) {
+                rec->fResult.lineTo(pts[0]);
+            }
+
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    rec->fResult.lineTo (pts[1]);
+                    rec->fPrev = pts[1];
+                    break;
+                case SkPath::kQuad_Verb:
+                    rec->fResult.quadTo(pts[1], pts[2]);
+                    rec->fPrev = pts[2];
+                    break;
+                case SkPath::kCubic_Verb:
+                    rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
+                    rec->fPrev = pts[3];
+                    break;
+                default: break;
+            }
+            addLineTo = true;
+        }
+    }, &rec);
+
+    rec.fResult.transform(mx);
+    return rec.fResult;
+}
+
+static SkPath clip(const SkPath& path, const SkHalfPlane& plane) {
+    SkPoint pts[2];
+    if (plane.twoPts(pts)) {
+        return clip(path, pts[0], pts[1]);
+    } else {
+        return SkPath();
+    }
+}
+
+static void draw_halfplane(SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
+    SkVector v = p1 - p0;
+    p0 = p0 - v * 1000;
+    p1 = p1 + v * 1000;
+
+    SkPaint paint;
+    paint.setColor(c);
+    canvas->drawLine(p0, p1, paint);
+}
+
+static SkPath make_path() {
+    SkRandom rand;
+    auto rand_pt = [&rand]() { return SkPoint{rand.nextF() * 400, rand.nextF() * 400}; };
+
+    SkPath path;
+    for (int i = 0; i < 4; ++i) {
+        path.moveTo(rand_pt()).quadTo(rand_pt(), rand_pt())
+            .quadTo(rand_pt(), rand_pt()).lineTo(rand_pt());
+    }
+    return path;
+}
+
+class HalfPlaneView : public Sample {
+    SkPoint fPts[2];
+    SkPath fPath;
+
+    SkString name() override { return SkString("halfplane"); }
+
+    void onOnceBeforeDraw() override {
+        fPts[0] = {0, 0};
+        fPts[1] = {3, 2};
+        fPath = make_path();
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        SkPaint paint;
+
+        paint.setColor({0.5f, 0.5f, 0.5f, 1.0f}, nullptr);
+        canvas->drawPath(fPath, paint);
+
+        paint.setColor({0, 0, 0, 1}, nullptr);
+        canvas->drawPath(clip(fPath, fPts[0], fPts[1]), paint);
+
+        draw_halfplane(canvas, fPts[0], fPts[1], SK_ColorRED);
+    }
+
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        return new Click;
+    }
+
+    bool onClick(Click* click) override {
+        fPts[0] = click->fCurr;
+        fPts[1] = fPts[0] + SkPoint{3, 2};
+        return true;
+    }
+};
+DEF_SAMPLE( return new HalfPlaneView(); )
+
+static void draw_halfplane(SkCanvas* canvas, const SkHalfPlane& p, SkColor c) {
+    SkPoint pts[2];
+    p.twoPts(pts);
+    draw_halfplane(canvas, pts[0], pts[1], c);
+}
+
+static void compute_half_planes(const SkMatrix& mx, SkScalar W, SkScalar H,
+                                SkHalfPlane planes[4]) {
+    SkScalar a = mx[0], b = mx[1], c = mx[2],
+             d = mx[3], e = mx[4], f = mx[5],
+             g = mx[6], h = mx[7], i = mx[8];
+
+    planes[0] = { 2*g - 2*a/W,  2*h - 2*b/W,  2*i - 2*c/W };
+    planes[1] = { 2*a/W,        2*b/W,        2*c/W };
+    planes[2] = { 2*g - 2*d/H,  2*h - 2*e/H,  2*i - 2*f/H };
+    planes[3] = { 2*d/H,        2*e/H,        2*f/H };
+}
+
+class HalfPlaneView2 : public Sample {
+    SkPoint fPts[4];
+    SkPath fPath;
+
+    SkString name() override { return SkString("halfplane2"); }
+
+    void onOnceBeforeDraw() override {
+        fPath = make_path();
+        SkRect r = fPath.getBounds();
+        r.toQuad(fPts);
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        SkMatrix mx;
+        {
+            SkRect r = fPath.getBounds();
+            SkPoint src[4];
+            r.toQuad(src);
+            mx.setPolyToPoly(src, fPts, 4);
+        }
+
+        SkPaint paint;
+        canvas->drawPath(fPath, paint);
+
+        canvas->save();
+        canvas->concat(mx);
+        paint.setColor(0x40FF0000);
+        canvas->drawPath(fPath, paint);
+        canvas->restore();
+
+        // draw the frame
+        paint.setStrokeWidth(10);
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPts, paint);
+
+        // draw the half-planes
+        SkHalfPlane planes[4];
+        compute_half_planes(mx, 400, 400, planes);
+        for (auto& p : planes) {
+            draw_halfplane(canvas, p, SK_ColorRED);
+        }
+    }
+
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        SkScalar r = 8;
+        SkRect rect = SkRect::MakeXYWH(x - r, y - r, 2*r, 2*r);
+        for (int i = 0; i < 4; ++i) {
+            if (rect.contains(fPts[i].fX, fPts[i].fY)) {
+                Click* c = new Click;
+                c->fMeta.setS32("index", i);
+                return c;
+            }
+        }
+        return nullptr;
+    }
+
+    bool onClick(Click* click) override {
+        int32_t index;
+        SkAssertResult(click->fMeta.findS32("index", &index));
+        SkASSERT(index >= 0 && index < 4);
+        fPts[index] = click->fCurr;
+        return true;
+    }
+};
+DEF_SAMPLE( return new HalfPlaneView2(); )
+
+#include "include/core/SkMatrix44.h"
+#include "include/utils/Sk3D.h"
+#include "tools/Resources.h"
+
+static SkMatrix44 inv(const SkMatrix44& m) {
+    SkMatrix44 inverse;
+    SkAssertResult(m.invert(&inverse));
+    return inverse;
+}
+
+static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPlane planes[6]) {
+    float mx[16];
+    m44.asColMajorf(mx);
+
+    SkScalar a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
+             e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
+             i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
+             m = mx[3], n = mx[7], /* o = mx[11], */ p = mx[15];
+
+    a = 2*a/W - m;  b = 2*b/W - n;  d = 2*d/W - p;
+    e = 2*e/H - m;  f = 2*f/H - n;  h = 2*h/H - p;
+    i = 2*i   - m;  j = 2*j   - n;  l = 2*l   - p;
+
+    planes[0] = { m - a, n - b, p - d }; // w - x
+    planes[1] = { m + a, n + b, p + d }; // w + x
+    planes[2] = { m - e, n - f, p - h }; // w - y
+    planes[3] = { m + e, n + f, p + h }; // w + y
+    planes[4] = { m - i, n - j, p - l }; // w - z
+    planes[5] = { m + i, n + j, p + l }; // w + z
+}
+
+class HalfPlaneView3 : public Sample {
+    float   fNear = 0.05f;
+    float   fFar = 4;
+    float   fAngle = SK_ScalarPI / 4;
+
+    SkPoint3    fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
+    SkPoint3    fCOA { 0, 0, 0 };
+    SkPoint3    fUp  { 0, 1, 0 };
+
+    SkMatrix44  fRot;
+
+    SkPath fPath;
+    sk_sp<SkShader> fShader;
+
+    SkString name() override { return SkString("halfplane3"); }
+
+    void onOnceBeforeDraw() override {
+        fPath = make_path();
+        fShader = GetResourceAsImage("images/mandrill_128.png")
+                        ->makeShader(SkMatrix::MakeScale(3, 3));
+    }
+
+    void rotate(float x, float y, float z) {
+        SkMatrix44 r;
+        if (x) {
+            r.setRotateAboutUnit(1, 0, 0, x);
+        } else if (y) {
+            r.setRotateAboutUnit(0, 1, 0, y);
+        } else {
+            r.setRotateAboutUnit(0, 0, 1, z);
+        }
+        fRot.postConcat(r);
+    }
+
+    SkMatrix44 get44() const {
+        SkMatrix44  camera,
+                    perspective,
+                    viewport;
+
+        Sk3Perspective(&perspective, fNear, fFar, fAngle);
+        Sk3LookAt(&camera, fEye, fCOA, fUp);
+        viewport.setScale(200, 200, 1).postTranslate( 200,  200, 0);
+
+        return viewport * perspective * camera * fRot * inv(viewport);
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        SkMatrix44 mx44 = this->get44();
+        SkMatrix mx = mx44;
+
+        SkPaint paint;
+        paint.setColor({0.75, 0.75, 0.75, 1});
+        canvas->drawPath(fPath, paint);
+
+        paint.setShader(fShader);
+
+        canvas->save();
+        canvas->concat(mx);
+        paint.setAlphaf(0.33f);
+        canvas->drawPath(fPath, paint);
+        paint.setAlphaf(1.f);
+        canvas->restore();
+
+        SkHalfPlane planes[6];
+        half_planes(mx44, 400, 400, planes);
+
+        SkPath path = clip(fPath, planes[4]);
+        canvas->save();
+        canvas->concat(mx);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+
+//        for (auto& p : planes) {
+//            draw_halfplane(canvas, p, SK_ColorRED);
+//        }
+        draw_halfplane(canvas, planes[4], SK_ColorBLUE);
+        draw_halfplane(canvas, planes[5], SK_ColorGREEN);
+    }
+
+    bool onChar(SkUnichar uni) override {
+        float delta = SK_ScalarPI / 30;
+        switch (uni) {
+            case '8': this->rotate( delta, 0, 0); return true;
+            case '2': this->rotate(-delta, 0, 0); return true;
+            case '4': this->rotate(0,  delta, 0); return true;
+            case '6': this->rotate(0, -delta, 0); return true;
+            case '-': this->rotate(0, 0,  delta); return true;
+            case '+': this->rotate(0, 0, -delta); return true;
+
+            case 'i': fEye.fZ += 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
+            case 'k': fEye.fZ -= 0.1f; SkDebugf("ez %g\n", fEye.fZ); return true;
+
+            case 'n': fNear += 0.1f; SkDebugf("near %g\n", fNear); return true;
+            case 'N': fNear -= 0.1f; SkDebugf("near %g\n", fNear); return true;
+            case 'f': fFar  += 0.1f; SkDebugf("far  %g\n", fFar); return true;
+            case 'F': fFar  -= 0.1f; SkDebugf("far  %g\n", fFar); return true;
+            default: break;
+        }
+        return false;
+    }
+    Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
+        return nullptr;
+    }
+
+    bool onClick(Click* click) override {
+        return false;
+    }
+};
+DEF_SAMPLE( return new HalfPlaneView3(); )
diff --git a/samplecode/SampleClock.cpp b/samplecode/SampleClock.cpp
index a90ecf1..f7332ce 100644
--- a/samplecode/SampleClock.cpp
+++ b/samplecode/SampleClock.cpp
@@ -160,7 +160,7 @@
 #ifdef USE_PATH
         path.reset();
         path.arcTo(rect, 0, 0, false);
-        path.addOval(rect, SkPath::kCCW_Direction);
+        path.addOval(rect, SkPathDirection::kCCW);
         path.arcTo(rect, 360, 0, true);
         canvas->drawPath(path, paintFill);
 #else
@@ -170,7 +170,7 @@
 #ifdef USE_PATH
         path.reset();
         path.arcTo(rect, 0, 0, false);
-        path.addOval(rect, SkPath::kCCW_Direction);
+        path.addOval(rect, SkPathDirection::kCCW);
         path.arcTo(rect, 360, 0, true);
         canvas->drawPath(path, paintStroke);
 #else
@@ -180,7 +180,7 @@
 #ifdef USE_PATH
         rect = SkRect::MakeLTRB(-6, -6, 6, 6);
         path.arcTo(rect, 0, 0, false);
-        path.addOval(rect, SkPath::kCCW_Direction);
+        path.addOval(rect, SkPathDirection::kCCW);
         path.arcTo(rect, 360, 0, true);
         canvas->drawPath(path, paintFill);
 #else
@@ -196,7 +196,7 @@
 #ifdef USE_PATH
         path.reset();
         path.arcTo(rect, 0, 0, false);
-        path.addOval(rect, SkPath::kCCW_Direction);
+        path.addOval(rect, SkPathDirection::kCCW);
         path.arcTo(rect, 360, 0, true);
         canvas->drawPath(path, paintStroke);
 #else
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
index 4e811a0..d903148 100644
--- a/samplecode/SampleComplexClip.cpp
+++ b/samplecode/SampleComplexClip.cpp
@@ -34,7 +34,7 @@
         path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150));
         path.lineTo(SkIntToScalar(50),  SkIntToScalar(150));
         path.close();
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
         SkColor pathColor = SK_ColorBLACK;
         SkPaint pathPaint;
         pathPaint.setAntiAlias(true);
@@ -99,8 +99,8 @@
                 }
                 canvas->save();
                     // set clip
-                    clipA.setFillType(invA ? SkPath::kInverseEvenOdd_FillType :
-                                             SkPath::kEvenOdd_FillType);
+                    clipA.setFillType(invA ? SkPathFillType::kInverseEvenOdd :
+                                             SkPathFillType::kEvenOdd);
                     canvas->clipPath(clipA);
                     canvas->clipPath(clipB, gOps[op].fOp);
 
diff --git a/samplecode/SampleDegenerateQuads.cpp b/samplecode/SampleDegenerateQuads.cpp
index 131e119..3d48cec 100644
--- a/samplecode/SampleDegenerateQuads.cpp
+++ b/samplecode/SampleDegenerateQuads.cpp
@@ -409,7 +409,7 @@
         static const GrQuadPerEdgeAA::VertexSpec kSpec =
             {GrQuad::Type::kGeneral, GrQuadPerEdgeAA::ColorType::kNone,
              GrQuad::Type::kAxisAligned, false, GrQuadPerEdgeAA::Domain::kNo,
-             GrAAType::kCoverage, false};
+             GrAAType::kCoverage, false, GrQuadPerEdgeAA::IndexBufferOption::kPictureFramed};
         static const GrQuad kIgnored(SkRect::MakeEmpty());
 
         GrQuadAAFlags flags = GrQuadAAFlags::kNone;
@@ -421,8 +421,9 @@
         GrQuad quad = GrQuad::MakeFromSkQuad(fCorners, SkMatrix::I());
 
         float vertices[56]; // 2 quads, with x, y, coverage, and geometry domain (7 floats x 8 vert)
-        GrQuadPerEdgeAA::Tessellate(vertices, kSpec, quad, {1.f, 1.f, 1.f, 1.f},
-                GrQuad(SkRect::MakeEmpty()), SkRect::MakeEmpty(), flags);
+        GrQuadPerEdgeAA::Tessellator tessellator(kSpec, (char*) vertices);
+        tessellator.append(&quad, nullptr, {1.f, 1.f, 1.f, 1.f},
+                           SkRect::MakeEmpty(), flags);
 
         // The first quad in vertices is the inset, then the outset, but they
         // are ordered TL, BL, TR, BR so un-interleave coverage and re-arrange
diff --git a/samplecode/SampleEffects.cpp b/samplecode/SampleEffects.cpp
index 1b21d13..f22aea1 100644
--- a/samplecode/SampleEffects.cpp
+++ b/samplecode/SampleEffects.cpp
@@ -82,10 +82,6 @@
             gPaintProcs[i](&fPaint[i]);
         }
 
-        SkColorMatrix cm;
-        cm.setRotate(SkColorMatrix::kG_Axis, 180);
-        cm.setIdentity();
-
         this->setBGColor(0xFFDDDDDD);
     }
 
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
index 58e954a..230f41b 100644
--- a/samplecode/SampleFillType.cpp
+++ b/samplecode/SampleFillType.cpp
@@ -27,7 +27,7 @@
 protected:
     virtual SkString name() { return SkString("FillType"); }
 
-    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
+    void showPath(SkCanvas* canvas, int x, int y, SkPathFillType ft,
                   SkScalar scale, const SkPaint& paint) {
 
         const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
@@ -45,13 +45,13 @@
     }
 
     void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
-        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
+        showPath(canvas,   0,   0, SkPathFillType::kWinding,
                  scale, paint);
-        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
+        showPath(canvas, 200,   0, SkPathFillType::kEvenOdd,
                  scale, paint);
-        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
+        showPath(canvas,  00, 200, SkPathFillType::kInverseWinding,
                  scale, paint);
-        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
+        showPath(canvas, 200, 200, SkPathFillType::kInverseEvenOdd,
                  scale, paint);
     }
 
diff --git a/samplecode/SampleFilterQuality.cpp b/samplecode/SampleFilterQuality.cpp
index 54fe4af..f18a1a6 100644
--- a/samplecode/SampleFilterQuality.cpp
+++ b/samplecode/SampleFilterQuality.cpp
@@ -42,7 +42,7 @@
     canvas->drawColor(SK_ColorWHITE);
 
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     path.addRect(SkRect::MakeWH(N/2, N));
     path.addRect(SkRect::MakeWH(N, N/2));
diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp
index 9785e88..546c6c4 100644
--- a/samplecode/SampleLayerMask.cpp
+++ b/samplecode/SampleLayerMask.cpp
@@ -42,7 +42,7 @@
         } else {
             SkPath p;
             p.addOval(r);
-            p.setFillType(SkPath::kInverseWinding_FillType);
+            p.setFillType(SkPathFillType::kInverseWinding);
             paint.setBlendMode(SkBlendMode::kDstOut);
             canvas->drawPath(p, paint);
         }
diff --git a/samplecode/SampleParagraph.cpp b/samplecode/SampleParagraph.cpp
index e377fd6..cc10e03 100644
--- a/samplecode/SampleParagraph.cpp
+++ b/samplecode/SampleParagraph.cpp
@@ -21,6 +21,7 @@
 #include "samplecode/Sample.h"
 #include "src/core/SkOSFile.h"
 #include "src/shaders/SkColorShader.h"
+#include "src/utils/SkOSPath.h"
 #include "src/utils/SkUTF.h"
 #include "tools/Resources.h"
 
@@ -33,7 +34,7 @@
         // If we reset font collection we need to reset paragraph cache
         static sk_sp<TestFontCollection> fFC = nullptr;
         if (fFC == nullptr) {
-            fFC = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str());
+            fFC = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
         }
         return fFC;
     }
@@ -44,7 +45,16 @@
     SkPoint pts[] = {{r.fLeft, r.fTop}, {r.fRight, r.fTop}};
     return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
 }
-
+/*
+void writeHtml(const char* name, Paragraph* paragraph) {
+        SkString tmpDir = skiatest::GetTmpDir();
+        if (!tmpDir.isEmpty()) {
+            SkString path = SkOSPath::Join(tmpDir.c_str(), name);
+            SkFILEWStream file(path.c_str());
+            file.write(nullptr, 0);
+        }
+}
+*/
 }  // namespace
 
 class ParagraphView1 : public ParagraphView_Base {
@@ -1118,30 +1128,34 @@
         canvas->clipRect(SkRect::MakeWH(w, h));
         canvas->drawColor(background);
 
+        auto fontCollection = sk_make_sp<FontCollection>();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+
         const char* text =
                 "( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
                 " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)("
                 " ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)( ´・‿・`)";
-
+        auto multiplier = 5.67;
         ParagraphStyle paragraphStyle;
         paragraphStyle.setTextAlign(TextAlign::kLeft);
         paragraphStyle.setMaxLines(10);
         paragraphStyle.turnHintingOff();
         TextStyle textStyle;
         textStyle.setFontFamilies({SkString("Roboto")});
-        textStyle.setFontSize(50);
+        textStyle.setFontSize(5 * multiplier);
         textStyle.setHeight(1.3f);
         textStyle.setColor(SK_ColorBLACK);
         textStyle.setFontStyle(SkFontStyle(SkFontStyle::kMedium_Weight, SkFontStyle::kNormal_Width,
                                            SkFontStyle::kUpright_Slant));
 
-        ParagraphBuilderImpl builder(paragraphStyle, getFontCollection());
+        ParagraphBuilderImpl builder(paragraphStyle, fontCollection);
         builder.pushStyle(textStyle);
         builder.addText(text, strlen(text));
         builder.pop();
 
         auto paragraph = builder.Build();
-        paragraph->layout(550);
+        paragraph->layout(200 * multiplier);
 
         std::vector<size_t> sizes = {0, 1, 2, 8, 19, 21, 22, 30, 150};
 
@@ -1152,7 +1166,7 @@
         RectWidthStyle rect_width_style = RectWidthStyle::kTight;
 
         for (size_t i = 0; i < sizes.size() - 1; ++i) {
-            size_t from = (i == 0 ? 0 : 1) + sizes[i];
+            size_t from = sizes[i];
             size_t to = sizes[i + 1];
             auto boxes = paragraph->getRectsForRange(from, to, rect_height_style, rect_width_style);
             if (boxes.empty()) {
@@ -1190,18 +1204,22 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         canvas->drawColor(SK_ColorWHITE);
-
+        auto multiplier = 5.67;
         const char* text = "English English 字典 字典 😀😃😄 😀😃😄";
+
+        auto fontCollection = sk_make_sp<FontCollection>();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+
         ParagraphStyle paragraph_style;
         paragraph_style.turnHintingOff();
-        ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
 
         TextStyle text_style;
         text_style.setFontFamilies({SkString("Roboto"),
                                     SkString("Noto Color Emoji"),
-                                    SkString("Source Han Serif CN")});
-        text_style.setColor(SK_ColorRED);
-        text_style.setFontSize(60);
+                                    SkString("Noto Serif CJK JP")});
+        text_style.setFontSize(10 * multiplier);
         text_style.setLetterSpacing(0);
         text_style.setWordSpacing(0);
         text_style.setColor(SK_ColorBLACK);
@@ -1214,10 +1232,6 @@
         paragraph->layout(width());
 
         paragraph->paint(canvas, 0, 0);
-        SkDEBUGCODE(auto impl = reinterpret_cast<ParagraphImpl*>(paragraph.get()));
-        SkASSERT(impl->runs().size() == 3);
-        SkASSERT(impl->runs()[0].textRange().end == impl->runs()[1].textRange().start);
-        SkASSERT(impl->runs()[1].textRange().end == impl->runs()[2].textRange().start);
     }
 
 private:
@@ -1232,34 +1246,67 @@
         canvas->drawColor(SK_ColorWHITE);
 
         auto text = "\U0001f469\U0000200D\U0001f469\U0000200D\U0001f466\U0001f469\U0000200D\U0001f469\U0000200D\U0001f467\U0000200D\U0001f467\U0001f1fa\U0001f1f8";
-            TextStyle text_style;
-            text_style.setFontFamilies({SkString("Ahem")});
-            text_style.setColor(SK_ColorBLACK);
-            text_style.setFontSize(60);
-            text_style.setLetterSpacing(0);
-            text_style.setWordSpacing(0);
-            ParagraphStyle paragraph_style;
-            paragraph_style.setTextStyle(text_style);
-            ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
-            builder.addText(text, strlen(text));
-            auto paragraph = builder.Build();
-            paragraph->layout(1000);
-            paragraph->paint(canvas, 0, 0);
 
-            SkColor colors[] = { SK_ColorRED, SK_ColorBLACK, SK_ColorBLUE, SK_ColorTRANSPARENT, SK_ColorTRANSPARENT };
-            SkPoint queries[] = {{ 1, 3},{1, 5}, {1, 9}, { 1, 17}, {1, 33}};
-            SkPaint paint;
-            paint.setColor(SK_ColorRED);
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setAntiAlias(true);
-            paint.setStrokeWidth(5);
-            for (auto& query : queries) {
-                auto rects = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
-                paint.setColor(colors[&query - &queries[0]]);
-                for (auto& rect: rects) {
-                    canvas->drawRect(rect.rect, paint);
-                }
+        TextStyle text_style;
+        text_style.setFontFamilies({SkString("Ahem")});
+        text_style.setColor(SK_ColorBLACK);
+        text_style.setFontSize(60);
+        text_style.setLetterSpacing(0);
+        text_style.setWordSpacing(0);
+        ParagraphStyle paragraph_style;
+        paragraph_style.setTextStyle(text_style);
+
+        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), true, true);
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+        builder.addText(text, strlen(text));
+        auto paragraph = builder.Build();
+        paragraph->layout(1000);
+        paragraph->paint(canvas, 0, 0);
+
+        struct pair {
+            unsigned fX;
+            unsigned fY;
+        };
+
+        pair hit1[] =
+              {{ 0, 8},{1, 33}, {2, 34}, { 3, 19}, {4, 20},
+               { 5, 21}, { 6, 22 }, { 7, 23 }, {8, 24 }, { 9, 25},
+               { 10, 26}, { 11, 27}, {12, 28}, { 13, 21}, {14, 22 },
+               { 15, 23}, {16, 24}, {17, 21}, { 18, 22}, {19, 21},
+               { 20, 24}, { 21, 23}, };
+
+        pair miss[] =
+              {{ 0, 4},{1, 17}, {2, 18}, { 3, 11}, {4, 12},
+               { 5, 13}, { 6, 14 }, { 7, 15 }, {8, 16 }, { 9, 17},
+               { 10, 18}, { 11, 19}, {12, 20}, { 13, 17}, {14, 18 },
+               { 15, 19}, {16, 20}, {17, 19}, { 18, 20},
+               { 20, 22}, };
+
+        auto rects = paragraph->getRectsForRange(7, 9, RectHeightStyle::kTight, RectWidthStyle::kTight);
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(1);
+        if (!rects.empty()) {
+            canvas->drawRect(rects[0].rect, paint);
+        }
+
+        for (auto& query : hit1) {
+            auto rects = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
+            if (rects.size() >= 1 && rects[0].rect.width() > 0) {
+            } else {
+                SkDebugf("+[%d:%d): Bad\n", query.fX, query.fY);
             }
+        }
+
+        for (auto& query : miss) {
+            auto miss = paragraph->getRectsForRange(query.fX, query.fY, RectHeightStyle::kTight, RectWidthStyle::kTight);
+            if (miss.empty()) {
+            } else {
+                SkDebugf("-[%d:%d): Bad\n", query.fX, query.fY);
+            }
+        }
     }
 
 private:
@@ -1303,65 +1350,6 @@
     typedef Sample INHERITED;
 };
 
-class ParagraphView13 : public ParagraphView_Base {
-protected:
-    SkString name() override { return SkString("Paragraph13"); }
-
-    void onDrawContent(SkCanvas* canvas) override {
-        canvas->drawColor(SK_ColorWHITE);
-
-        const char* text = "This\n"
-                           "is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.";
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Ahem")});
-        text_style.setColor(SK_ColorBLACK);
-        text_style.setFontSize(10);
-
-        auto relayout = [&](size_t lines, bool ellipsis,
-                SkScalar width, SkScalar height, SkScalar minWidth, SkScalar maxWidth, SkColor bg) {
-            ParagraphStyle paragraph_style;
-            SkPaint paint;
-            paint.setColor(bg);
-            text_style.setForegroundColor(paint);
-            paragraph_style.setTextStyle(text_style);
-            paragraph_style.setMaxLines(lines);
-            if (ellipsis) {
-                paragraph_style.setEllipsis(u"\u2026");
-            }
-            ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
-            builder.addText(text);
-            auto paragraph = builder.Build();
-            paragraph->layout(50);
-            paragraph->paint(canvas, 0, 0);
-            canvas->translate(0, paragraph->getHeight() + 10);
-
-            SkASSERT(width == paragraph->getMaxWidth());
-            SkASSERT(height == paragraph->getHeight());
-            SkASSERT(minWidth == paragraph->getMinIntrinsicWidth());
-            SkASSERT(maxWidth == paragraph->getMaxIntrinsicWidth());
-        };
-
-        SkPaint paint;
-        paint.setColor(SK_ColorLTGRAY);
-        canvas->drawRect(SkRect::MakeXYWH(0, 0, 50, 500), paint);
-
-        relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
-        relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
-        relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
-
-        relayout(1, true, 50, 10, 950, 950, SK_ColorYELLOW);
-        relayout(3, true, 50, 30,  50, 950, SK_ColorMAGENTA);
-        relayout(std::numeric_limits<size_t>::max(), true, 50, 20,  950, 950, SK_ColorCYAN);
-
-        relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
-        relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
-        relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
-    }
-
-private:
-    typedef Sample INHERITED;
-};
-
 class ParagraphView14 : public ParagraphView_Base {
 protected:
     SkString name() override { return SkString("Paragraph14"); }
@@ -1403,50 +1391,312 @@
 
     void onDrawContent(SkCanvas* canvas) override {
         canvas->drawColor(SK_ColorWHITE);
-        TextStyle text_style;
-        text_style.setFontFamilies({SkString("Ahem")});
-        text_style.setColor(SK_ColorBLACK);
-        ParagraphStyle paragraph_style;
-        paragraph_style.setTextStyle(text_style);
-        ParagraphBuilderImpl builder(paragraph_style, getFontCollection());
-        text_style.setFontSize(16);
-        builder.pushStyle(text_style);
-        builder.addText("C ");
-        text_style.setFontSize(20);
-        builder.pushStyle(text_style);
-        builder.addText("He");
-        builder.pop();
-        PlaceholderStyle placeholderStyle;
-        placeholderStyle.fHeight = 55.0f;
-        placeholderStyle.fWidth = 50.0f;
-        placeholderStyle.fBaseline = TextBaseline::kAlphabetic;
-        placeholderStyle.fAlignment = PlaceholderAlignment::kBottom;
-        builder.addPlaceholder(placeholderStyle);
-        text_style.setFontSize(16);
-        builder.pushStyle(text_style);
-        builder.addText("hello world! sieze the day!");
-        auto paragraph = builder.Build();
-        paragraph->layout(400);
-        paragraph->paint(canvas, 0, 0);
-        SkPaint paint;
-        paint.setColor(SK_ColorRED);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(1);
-        canvas->drawRect(SkRect::MakeXYWH(0, 0, 400, 200), paint);
 
-        auto phs = paragraph->getRectsForPlaceholders();
-        for (auto& ph : phs) {
-            paint.setStyle(SkPaint::kFill_Style);
-            paint.setColor(SK_ColorYELLOW);
-            canvas->drawRect(ph.rect, paint);
-        }
+        TextStyle text_style;
+        text_style.setFontFamilies({SkString("abc.ttf")});
+        text_style.setFontSize(50);
+
+        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
+
+        fontCollection->addFontFromFile("abc/abc.ttf", "abc");
+        fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave");
+        fontCollection->addFontFromFile("abc/abc+agrave.ttf", "abc+agrave");
+
+        ParagraphStyle paragraph_style;
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+        text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
+        text_style.setColor(SK_ColorBLUE);
+        builder.pushStyle(text_style);
+        builder.addText(u"a\u0300");
+        text_style.setColor(SK_ColorMAGENTA);
+        builder.pushStyle(text_style);
+        builder.addText(u"à");
+
+        text_style.setFontFamilies({SkString("abc"), SkString("abc+agrave")});
+
+        text_style.setColor(SK_ColorRED);
+        builder.pushStyle(text_style);
+        builder.addText(u"a\u0300");
+        text_style.setColor(SK_ColorGREEN);
+        builder.pushStyle(text_style);
+        builder.addText(u"à");
+
+        auto paragraph = builder.Build();
+        paragraph->layout(800);
+        paragraph->paint(canvas, 50, 50);
+
     }
 
 private:
     typedef Sample INHERITED;
 };
 
+class ParagraphView16 : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("Paragraph16"); }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+
+        const char* text = "content";
+
+        ParagraphStyle paragraph_style;
+        paragraph_style.setMaxLines(1);
+        paragraph_style.setEllipsis(u"\u2026");
+        //auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+        auto fontCollection = sk_make_sp<FontCollection>();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+        TextStyle text_style;
+        text_style.setFontFamilies({SkString(".SF Pro Text")});
+        text_style.setColor(SK_ColorBLACK);
+        text_style.setFontSize(17.0f * 99.0f);
+        text_style.setLetterSpacing(0.41f);
+        builder.pushStyle(text_style);
+        builder.addText(text);
+
+        auto paragraph = builder.Build();
+        paragraph->layout(800);
+        paragraph->paint(canvas, 0, 0);
+    }
+
+private:
+    typedef Sample INHERITED;
+};
+
+class ParagraphView17 : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("Paragraph17"); }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+
+        auto fontCollection = sk_make_sp<FontCollection>();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+        auto navy = SkColorSetRGB(0, 0, 139);
+        auto ltgray = SkColorSetRGB(211, 211, 211);
+        auto multiplier = 5.67;
+
+        const char* text = ">Sͬ͑̀͐̈͒̈́̋̎ͮͩ̽̓ͬ̂̆̔͗́̓ͣͧ͊ͫ͛̉͌̐̑ͪ͗̚͝҉̴͉͢k̡̊̓ͫͭͩ͂͊ͨͪͬ̑ͫ̍̌̄͛̌̂̑̂̋̊̔ͫ͛̽̑ͨ̍ͭ̓̀ͪͪ̉͐͗̌̓̃̚͟͝҉̢͏̫̞̙͇͖̮͕̗̟͕͇͚̻͈̣̻̪͉̰̲̣̫ͅͅP̴̅̍͒̿͗͗̇ͩ̃͆͌̀̽͏̧̡͕͖̝̖̼̺̰̣̬͔͖͔̼͙̞̦̫͓̘͜a̸̴̸̴̢̢̨̨̫͍͓̥̼̭̼̻̤̯̙̤̻̠͚̍̌͋̂ͦͨ̽̇͌͌͆̀̽̎͒̄ͪ̐ͦ̈ͫ͐͗̓̚̚͜ͅr͐͐ͤͫ̐ͥ͂̈́̿́ͮ̃͗̓̏ͫ̀̿͏̸̵̧́͘̕͟͝͠͞͠҉̷̧͚͢͟a̓̽̎̄͗̔͛̄̐͊͛ͫ͂͌̂̂̈̈̓̔̅̅̄͊̉́ͪ̑̄͆ͬ̍͆ͭ͋̐ͬ͏̷̵̨̢̩̹̖͓̥̳̰͔̱̬͖̙͓̙͇̀̀̕͜͟͟͢͟͜͠͡g̨̅̇ͦ͋̂ͦͨͭ̓͐͆̏̂͛̉ͧ̑ͫ̐̒͛ͫ̍̒͛́̚҉̷̨̛̛̀͜͢͞҉̩̘̲͍͎̯̹̝̭̗̱͇͉̲̱͔̯̠̹̥̻͉̲̜̤̰̪̗̺̖̺r̷͌̓̇̅ͭ̀̐̃̃ͭ͑͗̉̈̇̈́ͥ̓ͣ́ͤ͂ͤ͂̏͌̆̚҉̴̸̧̢̢̛̫͉̦̥̤̙͈͉͈͉͓̙̗̟̳̜͈̗̺̟̠̠͖͓̖̪͕̠̕̕͝ͅả̸̴̡̡̧͠͞͡͞҉̛̕͟͏̷̘̪̱͈̲͉̞̠̞̪̫͎̲̬̖̀̀͟͝͞͞͠p̛͂̈͐̚͠҉̵̸̡̢̢̩̹͙̯͖̙̙̮̥̙͚̠͔̥̭̮̞̣̪̬̥̠̖̝̥̪͎́̀̕͜͡͡ͅͅh̵̷̵̡̛ͤ̂͌̐̓̐̋̋͊̒̆̽́̀̀̀͢͠͞͞҉̷̸̢̕҉͚̯͖̫̜̞̟̠̱͉̝̲̹̼͉̟͉̩̮͔̤͖̞̭̙̹̬ͅ<";
+
+        ParagraphStyle paragraph_style;
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+        SkPaint paint;
+        paint.setColor(ltgray);
+        TextStyle text_style;
+        text_style.setBackgroundColor(paint);
+        text_style.setColor(navy);
+        text_style.setFontFamilies({SkString("Roboto")});
+        text_style.setFontSize(20 * multiplier);
+        builder.pushStyle(text_style);
+        builder.addText(text);
+        auto paragraph = builder.Build();
+        paragraph->layout(10000);
+        paragraph->paint(canvas, 0, 0);
+    }
+
+private:
+    typedef Sample INHERITED;
+};
+
+class Zalgo {
+    private:
+    std::u16string COMBINING_DOWN = u"\u0316\u0317\u0318\u0319\u031c\u031d\u031e\u031f\u0320\u0324\u0325\u0326\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0339\u033a\u033b\u033c\u0345\u0347\u0348\u0349\u034d\u034e\u0353\u0354\u0355\u0356\u0359\u035a\u0323";
+    std::u16string COMBINING_UP = u"\u030d\u030e\u0304\u0305\u033f\u0311\u0306\u0310\u0352\u0357\u0351\u0307\u0308\u030a\u0342\u0343\u0344\u034a\u034b\u034c\u0303\u0302\u030c\u0350\u0300\u0301\u030b\u030f\u0312\u0313\u0314\u033d\u0309\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u035b\u0346\u031a";
+    std::u16string COMBINING_MIDDLE = u"\u0315\u031b\u0340\u0341\u0358\u0321\u0322\u0327\u0328\u0334\u0335\u0336\u034f\u035c\u035d\u035e\u035f\u0360\u0362\u0338\u0337\u0361\u0489";
+
+    std::u16string randomMarks(std::u16string& combiningMarks) {
+        std::u16string result;
+        auto num = std::rand() % (combiningMarks.size() / 1);
+        for (size_t i = 0; i < num; ++i) {
+            auto index = std::rand() % combiningMarks.size();
+            result += combiningMarks[index];
+        }
+        return result;
+    }
+
+public:
+    std::u16string zalgo(std::string victim) {
+        std::u16string result;
+        for (auto& c : victim) {
+            result += c;
+            result += randomMarks(COMBINING_UP);
+            result += randomMarks(COMBINING_MIDDLE);
+            result += randomMarks(COMBINING_DOWN);
+        }
+        return result;
+    }
+};
+
+class ParagraphView18 : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("Paragraph18"); }
+
+    bool onChar(SkUnichar uni) override {
+            switch (uni) {
+                case ' ':
+                    fLimit = 400;
+                    return true;
+                case 's':
+                    fLimit += 10;
+                    return true;
+                case 'f':
+                    if (fLimit > 10) {
+                        fLimit -= 10;
+                    }
+                    return true;
+                default:
+                    break;
+            }
+            return false;
+    }
+
+    bool onAnimate(double nanos) override {
+        if (++fIndex > fLimit) {
+            fRedraw = true;
+            fIndex = 0;
+        } else {
+            fRepeat = true;
+        }
+        return true;
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+
+        auto navy = SkColorSetRGB(0, 0, 139);
+        auto ltgray = SkColorSetRGB(211, 211, 211);
+
+        auto multiplier = 5.67;
+        auto fontCollection = sk_make_sp<FontCollection>();
+        fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+        fontCollection->enableFontFallback();
+
+        ParagraphStyle paragraph_style;
+        TextStyle text_style;
+        text_style.setFontFamilies({SkString("Roboto")});
+        text_style.setFontSize(20 * multiplier);
+        text_style.setColor(navy);
+        SkPaint paint;
+        paint.setColor(ltgray);
+        text_style.setBackgroundColor(paint);
+
+        Zalgo zalgo;
+
+        if (fRedraw || fRepeat) {
+
+            if (fRedraw || fParagraph.get() == nullptr) {
+                ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+                builder.pushStyle(text_style);
+                auto utf16text = zalgo.zalgo("SkParagraph");
+                icu::UnicodeString unicode((UChar*)utf16text.data(), SkToS32(utf16text.size()));
+                std::string str;
+                unicode.toUTF8String(str);
+                SkDebugf("Text:>%s<\n", str.data());
+                builder.addText(utf16text);
+                fParagraph = builder.Build();
+            }
+
+            auto impl = static_cast<ParagraphImpl*>(fParagraph.get());
+            impl->setState(InternalState::kUnknown);
+            fParagraph->layout(1000);
+            fParagraph->paint(canvas, 300, 200);
+
+            for (auto& run : impl->runs()) {
+                SkString fontFamily("unresolved");
+                if (run.font().getTypeface() != nullptr) {
+                    run.font().getTypeface()->getFamilyName(&fontFamily);
+                }
+                if (run.font().getTypeface() != nullptr) {
+                    for (size_t i = 0; i < run.size(); ++i) {
+                        auto glyph = run.glyphs().begin() + i;
+                        if (*glyph == 0) {
+                            SkDebugf("Run[%d] @pos=%d\n", run.index(), i);
+                            SkASSERT(false);
+                        }
+                    }
+                } else {
+                    SkDebugf("Run[%d]: %s\n", run.index(), fontFamily.c_str());
+                    SkASSERT(false);
+                }
+            }
+            fRedraw = false;
+            fRepeat = false;
+        }
+    }
+
+private:
+    bool fRedraw = true;
+    bool fRepeat = false;
+    size_t fIndex = 0;
+    size_t fLimit = 20;
+    std::unique_ptr<Paragraph> fParagraph;
+    typedef Sample INHERITED;
+};
+
+class ParagraphView19 : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("Paragraph19"); }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+
+        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+
+        const char* text = "World domination is such an ugly phrase - I prefer to call it world optimisation";
+        ParagraphStyle paragraph_style;
+        paragraph_style.setMaxLines(7);
+        paragraph_style.setEllipsis(u"\u2026");
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+        TextStyle text_style;
+        text_style.setColor(SK_ColorBLACK);
+        text_style.setFontFamilies({SkString("Roboto")});
+        text_style.setFontSize(40);
+        builder.pushStyle(text_style);
+        builder.addText(text);
+        auto paragraph = builder.Build();
+        paragraph->layout(this->width());
+
+        paragraph->paint(canvas, 0, 0);
+    }
+
+private:
+    typedef Sample INHERITED;
+};
+
+class ParagraphView20 : public ParagraphView_Base {
+protected:
+    SkString name() override { return SkString("Paragraph20"); }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawColor(SK_ColorWHITE);
+
+        auto fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+
+        const char* text = "";
+        ParagraphStyle paragraph_style;
+        paragraph_style.setMaxLines(std::numeric_limits<size_t>::max());
+        //paragraph_style.setEllipsis(u"\u2026");
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+        TextStyle text_style;
+        text_style.setColor(SK_ColorBLACK);
+        text_style.setFontFamilies({SkString("Roboto")});
+        text_style.setFontSize(40);
+        builder.pushStyle(text_style);
+        builder.addText(text);
+        auto paragraph = builder.Build();
+        paragraph->layout(this->width());
+
+        paragraph->paint(canvas, 0, 0);
+    }
+
+private:
+    typedef Sample INHERITED;
+};
 //////////////////////////////////////////////////////////////////////////////
 
 DEF_SAMPLE(return new ParagraphView1();)
@@ -1461,6 +1711,10 @@
 DEF_SAMPLE(return new ParagraphView10();)
 DEF_SAMPLE(return new ParagraphView11();)
 DEF_SAMPLE(return new ParagraphView12();)
-DEF_SAMPLE(return new ParagraphView13();)
 DEF_SAMPLE(return new ParagraphView14();)
 DEF_SAMPLE(return new ParagraphView15();)
+DEF_SAMPLE(return new ParagraphView16();)
+DEF_SAMPLE(return new ParagraphView17();)
+DEF_SAMPLE(return new ParagraphView18();)
+DEF_SAMPLE(return new ParagraphView19();)
+DEF_SAMPLE(return new ParagraphView20();)
diff --git a/samplecode/SamplePathText.cpp b/samplecode/SamplePathText.cpp
index 273573a..0b8ef02 100644
--- a/samplecode/SamplePathText.cpp
+++ b/samplecode/SamplePathText.cpp
@@ -278,7 +278,7 @@
 
             SkPath* backpath = &fBackPaths[i];
             backpath->reset();
-            backpath->setFillType(SkPath::kEvenOdd_FillType);
+            backpath->setFillType(SkPathFillType::kEvenOdd);
 
             SkPath::RawIter iter(glyph.fPath);
             SkPath::Verb verb;
diff --git a/samplecode/SampleQuadStroker.cpp b/samplecode/SampleQuadStroker.cpp
index 72dfabc..6b679e2 100644
--- a/samplecode/SampleQuadStroker.cpp
+++ b/samplecode/SampleQuadStroker.cpp
@@ -488,7 +488,7 @@
         paint.setColor(0x3f0f1f3f);
         canvas->drawPath(path, paint);
         path.reset();
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
         path.addCircle(center.fX, center.fY, maxSide + width / 2);
         SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width,
                 (maxSide + width) * 2, (maxSide + width) * 2);
@@ -646,9 +646,9 @@
             path.reset();
             SkRRect rr2;
             rr.inset(width/2, width/2, &rr2);
-            path.addRRect(rr2, SkPath::kCCW_Direction);
+            path.addRRect(rr2, SkPathDirection::kCCW);
             rr.inset(-width/2, -width/2, &rr2);
-            path.addRRect(rr2, SkPath::kCW_Direction);
+            path.addRRect(rr2, SkPathDirection::kCW);
             SkPaint paint;
             paint.setAntiAlias(true);
             paint.setColor(0x40FF8844);
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
index 58ad5e0..7c16c01 100644
--- a/samplecode/SampleRegion.cpp
+++ b/samplecode/SampleRegion.cpp
@@ -32,7 +32,7 @@
     SkPath path;
     path.addRect(0.0f, 0.0f,
                  SkIntToScalar(width), SkIntToScalar(height),
-                 SkPath::kCW_Direction);
+                 SkPathDirection::kCW);
     SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
 
     SkCanvas c(bitmap);
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
index 09cb87c..d58f4f1 100644
--- a/samplecode/SampleSlides.cpp
+++ b/samplecode/SampleSlides.cpp
@@ -136,9 +136,9 @@
 
     path.reset();
     SkRect r = { 0, 0, 250, 120 };
-    path.addOval(r, SkPath::kCW_Direction);
+    path.addOval(r, SkPathDirection::kCW);
     r.inset(50, 50);
-    path.addRect(r, SkPath::kCCW_Direction);
+    path.addRect(r, SkPathDirection::kCCW);
 
     canvas->translate(320, 20);
     for (i = 0; i < SK_ARRAY_COUNT(gPE2); i++) {
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
index 4963f97..b22ba8d 100644
--- a/samplecode/SampleStrokePath.cpp
+++ b/samplecode/SampleStrokePath.cpp
@@ -112,8 +112,8 @@
             "Z";
         SkParsePath::FromSVGString(str, &fPath);
 #else
-        fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction);
-        fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction);
+        fPath.addCircle(0, 0, SkIntToScalar(50), SkPathDirection::kCW);
+        fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPathDirection::kCW);
 #endif
 
         scale_to_width(&fPath, fWidth);
@@ -192,11 +192,11 @@
         fPath.offset(100, 0);
 #endif
 
-        fPath.setFillType(SkPath::kWinding_FillType);
+        fPath.setFillType(SkPathFillType::kWinding);
         drawSet(canvas, &paint);
 
         canvas->translate(0, fPath.getBounds().height() * 5 / 4);
-        fPath.setFillType(SkPath::kEvenOdd_FillType);
+        fPath.setFillType(SkPathFillType::kEvenOdd);
         drawSet(canvas, &paint);
     }
 
diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp
index 2303288..8e57750 100644
--- a/samplecode/SampleTextEffects.cpp
+++ b/samplecode/SampleTextEffects.cpp
@@ -82,7 +82,7 @@
     virtual bool onFilterPath(SkPath* dst, const SkPath& src,
                               SkStrokeRec*, const SkRect*) const override {
         *dst = src;
-        dst->setFillType(SkPath::kInverseWinding_FillType);
+        dst->setFillType(SkPathFillType::kInverseWinding);
         return true;
     }
 
diff --git a/site/dev/contrib/cqkeywords.md b/site/dev/contrib/cqkeywords.md
index 8f0385a..7056178 100644
--- a/site/dev/contrib/cqkeywords.md
+++ b/site/dev/contrib/cqkeywords.md
@@ -1,19 +1,30 @@
 Commit Queue Keywords
 =====================
 
-COMMIT
+See [CQ
+documentation](https://chromium.googlesource.com/chromium/src/+/master/docs/infra/cq.md)
+for more information.
+
+Options in the form "Key: Value"  must appear in the last paragraph of the
+commit message to be used.
+
+
+Commit
 ------
 
 If you are working on experimental code and do not want to risk accidentally
-submitting the change via the CQ, then you can mark it with "COMMIT=false".
+submitting the change via the CQ, then you can mark it with "Commit: false".
 The CQ will immediately abandon the change if it contains this option.
-To do a dry run through the CQ please use Gerrit's [CQ Dry Run](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/G5-X0_tfmok) feature.
+To do a dry run through the CQ please use Gerrit's [CQ Dry
+Run](https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/G5-X0_tfmok)
+feature.
 
-    COMMIT=false
+    Commit: false
 
 The CQ will run through its list of verifiers (reviewer check, trybots, tree check,
 presubmit check), and will close the issue instead of committing it.
 
+
 No-Dependency-Checks
 --------------------
 
@@ -22,7 +33,8 @@
 The CQ rejects patchsets with open dependencies. An open dependency exists when a CL
 depends on another CL that is not yet closed. You can skip this check with this keyword.
 
-CQ_INCLUDE_TRYBOTS
+
+Cq-Include-Trybots
 ------------------
 
 Allows you to add arbitrary trybots to the CQ's list of default trybots.
@@ -30,25 +42,36 @@
 
 This is the format of the values of this keyword:
 
-    CQ_INCLUDE_TRYBOTS=bucket1:bot1,bot2;bucket2:bot3,bot4
+    Cq-Include-Trybots: bucket1:bot1,bot2;bucket2:bot3,bot4
+
+Multiple lines are allowed:
+
+    Cq-Include-Trybots: bucket1:bot1
+    Cq-Include-Trybots: bucket1:bot2
+    Cq-Include-Trybots: bucket2:bot3
+    Cq-Include-Trybots: bucket2:bot4
 
 Here are some real world examples:
 
-    CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_chromium_asan_rel_ng
+    Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_asan_rel_ng
+    Cq-Include-Trybots: skia.primary:Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE
+    Cq-Include-Trybots: luci.skia.skia.primary:Build-Debian9-Clang-x86-devrel-Android_SKQP
 
-    CQ_INCLUDE_TRYBOTS=skia.primary:Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE
+    FIXME: what bucket are skia bots in now?
 
 
 No-Tree-Checks
 --------------
 
-If you want to skip the tree status checks, to make the CQ commit a CL even if the tree is closed,
-you can add the following line to the CL description:
+If you want to skip the tree status checks, to make the CQ commit a CL even if
+the tree is closed, you can add the following line to the CL description:
 
     No-Tree-Checks: true
 
-This is discouraged, since the tree is closed for a reason. However, in rare cases this is acceptable,
-primarily to fix build breakages (i.e., your CL will help in reopening the tree).
+This is discouraged, since the tree is closed for a reason. However, in rare
+cases this is acceptable, primarily to fix build breakages (i.e., your CL will
+help in reopening the tree).
+
 
 No-Presubmit
 ------------
@@ -57,20 +80,14 @@
 
     No-Presubmit: true
 
+
 No-Try
 ------
 
-If you cannot wait for the try job results, you can add the following line to the CL description:
+If you cannot wait for the try job results, you can add the following line to
+the CL description:
 
     No-Try: true
 
-The CQ will then not run any try jobs for your change and will commit the CL as soon as the tree is open, assuming the presubmit check passes.
-
-NO_MERGE_BUILDS
----------------
-
-This keyword prevents the Skia build masters from building this commit with others. Use it when your
-commit may have effects that you don't want mis-attributed to other commits. Just include the keyword
-somewhere in the commit message:
-
-    NO_MERGE_BUILDS
+The CQ will then not run any try jobs for your change and will commit the CL as
+soon as the tree is open, assuming the presubmit check passes.
diff --git a/site/dev/testing/automated_testing.md b/site/dev/testing/automated_testing.md
index 16ff488..a862088 100644
--- a/site/dev/testing/automated_testing.md
+++ b/site/dev/testing/automated_testing.md
@@ -26,7 +26,6 @@
 have a `gen_tasks.go` which will generate `tasks.json`. You will need to
 [install Go](https://golang.org/doc/install). From the repository root:
 
-	$ go get -u go.skia.org/infra/...
 	$ go run infra/bots/gen_tasks.go
 
 It is necessary to run `gen_tasks.go` every time it is changed or every time an
diff --git a/site/dev/testing/swarmingbots.md b/site/dev/testing/swarmingbots.md
index a2626f3..70db06c 100644
--- a/site/dev/testing/swarmingbots.md
+++ b/site/dev/testing/swarmingbots.md
@@ -49,7 +49,7 @@
 run tryjobs (after adding debugging output to the relevant code). In some cases you may also need to
 [create or modify tryjobs](automated_testing#adding-new-jobs).
 
-For Googlers: If you need more control (e.g. to run GDB) and need run to directly on a swarming bot then you can use [leasing.skia.org](https://leasing.skia.org).<br/>
+For Googlers: If you need more control (e.g. to run GDB) and need to run directly on a swarming bot then you can use [leasing.skia.org](https://leasing.skia.org).<br/>
 If that does not work then the [current trooper][current trooper] can help you bring the device back to your desk and connect
 it to GoogleGuest Wifi or the [Google Test Network](http://go/gtn-criteria).
 
@@ -59,9 +59,12 @@
 If a permanent change needs to be made on the machine (such as an OS or driver update), please [file
 a bug][infra bug] and assign to jcgregorio for reassignment.
 
+For your convenience, the machine skolo-builder is available for checking out and compiling code within the Skolo. See
+more info in the [Skolo maintenance doc][remote access] remote access section.
+
 [current trooper]: http://skia-tree-status.appspot.com/trooper
 [remote access]:
-    https://docs.google.com/document/d/1zTR1YtrIFBo-fRWgbUgvJNVJ-s_4_sNjTrHIoX2vulo/edit#heading=h.2nq3yd1axg0n
+    https://docs.google.com/document/d/1zTR1YtrIFBo-fRWgbUgvJNVJ-s_4_sNjTrHIoX2vulo/edit#heading=h.v77cmwbwc5la
 [infra bug]: https://bugs.chromium.org/p/skia/issues/entry?template=Infrastructure+Bug
 [remotely debug android]: https://docs.google.com/document/d/1nxn7TobfaLNNfhSTiwstOnjV0jCxYUI1uwW0T_V7BYg/
 [vnc to skolo windows]:
diff --git a/site/dev/tools/markdown.md b/site/dev/tools/markdown.md
index fb49339..3aa4d09 100644
--- a/site/dev/tools/markdown.md
+++ b/site/dev/tools/markdown.md
@@ -23,13 +23,13 @@
 you are running on a Google corporate workstation. Installation also means
 that you have `$GOPATH/bin` [added to your PATH](https://golang.org/doc/code.html#GOPATH). Run:
 
-    go get -u go.skia.org/infra/doc/go/docserver
-    cd $GOPATH/src/go.skia.org/infra/doc
+    git clone https://skia.googlesource.com/buildbot
+    cd buildbot/docserverk
     make
 
 And then **from within** the directory of your local Git checkout of Skia run:
 
-    docserver --preview --local
+    docserverk --preview --local
 
 Then visit http://localhost:8000 to preview your changes. There is no need to
 restart the server for file changes, but you will need to restart it if there
@@ -39,7 +39,7 @@
 If port 8000 is unavailable on your machine you can set the port to use via
 the --port flag:
 
-    docserver --preview --local --port=:8002
+    docserverk --preview --local --port=:8002
 
 METADATA
 --------
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index 80ef553..32802f8 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -218,7 +218,6 @@
 
 void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target) {
     FlushInfo flushInfo;
-    SkExclusiveStrikePtr autoGlyphCache;
     auto& context = target->context()->internal();
     auto glyphCache = context.grContext()->priv().getGrStrikeCache();
     auto atlasManager = context.grContext()->priv().getAtlasManager();
@@ -232,10 +231,9 @@
     for (int i = 0; i < fGeoCount; ++i) {
         // TODO4F: Preserve float colors
         GrTextBlob::VertexRegenerator regenerator(
-                resourceProvider, fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun,
+                resourceProvider, fGeoData[i].fBlob, fGeoData[i].fSubRunPtr,
                 fGeoData[i].fViewMatrix, fGeoData[i].fX, fGeoData[i].fY,
-                fGeoData[i].fColor.toBytes_RGBA(), &context, glyphCache, atlasManager,
-                &autoGlyphCache);
+                fGeoData[i].fColor.toBytes_RGBA(), &context, glyphCache, atlasManager);
         bool done = false;
         while (!done) {
             GrTextBlob::VertexRegenerator::Result result;
diff --git a/src/c/sk_surface.cpp b/src/c/sk_surface.cpp
index b3f203f..65fe209 100644
--- a/src/c/sk_surface.cpp
+++ b/src/c/sk_surface.cpp
@@ -56,13 +56,13 @@
 
 const struct {
     sk_path_direction_t fC;
-    SkPath::Direction   fSk;
+    SkPathDirection   fSk;
 } gPathDirMap[] = {
-    { CW_SK_PATH_DIRECTION,  SkPath::kCW_Direction },
-    { CCW_SK_PATH_DIRECTION, SkPath::kCCW_Direction },
+    { CW_SK_PATH_DIRECTION,  SkPathDirection::kCW },
+    { CCW_SK_PATH_DIRECTION, SkPathDirection::kCCW },
 };
 
-static bool from_c_path_direction(sk_path_direction_t cdir, SkPath::Direction* dir) {
+static bool from_c_path_direction(sk_path_direction_t cdir, SkPathDirection* dir) {
     for (size_t i = 0; i < SK_ARRAY_COUNT(gPathDirMap); ++i) {
         if (gPathDirMap[i].fC == cdir) {
             if (dir) {
@@ -202,7 +202,7 @@
 }
 
 void sk_path_add_rect(sk_path_t* cpath, const sk_rect_t* crect, sk_path_direction_t cdir) {
-    SkPath::Direction dir;
+    SkPathDirection dir;
     if (!from_c_path_direction(cdir, &dir)) {
         return;
     }
@@ -210,7 +210,7 @@
 }
 
 void sk_path_add_oval(sk_path_t* cpath, const sk_rect_t* crect, sk_path_direction_t cdir) {
-    SkPath::Direction dir;
+    SkPathDirection dir;
     if (!from_c_path_direction(cdir, &dir)) {
         return;
     }
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index cc5b9bf..0b4580a 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -26,8 +26,8 @@
 #include "src/codec/SkWebpCodec.h"
 #ifdef SK_HAS_WUFFS_LIBRARY
 #include "src/codec/SkWuffsCodec.h"
-#else
-#include "src/codec/SkGifCodec.h"
+#elif defined(SK_USE_LIBGIFCODEC)
+#include "SkGifCodec.h"
 #endif
 
 struct DecoderProc {
@@ -45,7 +45,7 @@
     #endif
     #ifdef SK_HAS_WUFFS_LIBRARY
         { SkWuffsCodec_IsFormat, SkWuffsCodec_MakeFromStream },
-    #else
+    #elif defined(SK_USE_LIBGIFCODEC)
         { SkGifCodec::IsGif, SkGifCodec::MakeFromStream },
     #endif
     #ifdef SK_HAS_PNG_LIBRARY
diff --git a/src/codec/SkGifCodec.cpp b/src/codec/SkGifCodec.cpp
deleted file mode 100644
index b2185d1..0000000
--- a/src/codec/SkGifCodec.cpp
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/*
- * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "include/codec/SkCodecAnimation.h"
-#include "include/core/SkStream.h"
-#include "include/private/SkColorData.h"
-#include "src/codec/SkCodecPriv.h"
-#include "src/codec/SkColorTable.h"
-#include "src/codec/SkGifCodec.h"
-#include "src/codec/SkSwizzler.h"
-#include "src/core/SkMakeUnique.h"
-
-#include <algorithm>
-
-#define GIF87_STAMP "GIF87a"
-#define GIF89_STAMP "GIF89a"
-#define GIF_STAMP_LEN 6
-
-/*
- * Checks the start of the stream to see if the image is a gif
- */
-bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) {
-    if (bytesRead >= GIF_STAMP_LEN) {
-        if (memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
-            memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-/*
- * Error function
- */
-static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
-    SkCodecPrintf("Gif Error: %s\n", msg);
-    return result;
-}
-
-std::unique_ptr<SkCodec> SkGifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
-                                                    Result* result) {
-    std::unique_ptr<SkGifImageReader> reader(new SkGifImageReader(std::move(stream)));
-    *result = reader->parse(SkGifImageReader::SkGIFSizeQuery);
-    if (*result != kSuccess) {
-        return nullptr;
-    }
-
-    // If no images are in the data, or the first header is not yet defined, we cannot
-    // create a codec. In either case, the width and height are not yet known.
-    auto* frame = reader->frameContext(0);
-    if (!frame || !frame->isHeaderDefined()) {
-        *result = kInvalidInput;
-        return nullptr;
-    }
-
-    // isHeaderDefined() will not return true if the screen size is empty.
-    SkASSERT(reader->screenHeight() > 0 && reader->screenWidth() > 0);
-
-    const auto alpha = reader->firstFrameHasAlpha() ? SkEncodedInfo::kBinary_Alpha
-                                                    : SkEncodedInfo::kOpaque_Alpha;
-    // Use kPalette since Gifs are encoded with a color table.
-    // FIXME: Gifs can actually be encoded with 4-bits per pixel. Using 8 works, but we could skip
-    //        expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
-    auto encodedInfo = SkEncodedInfo::Make(reader->screenWidth(), reader->screenHeight(),
-                                           SkEncodedInfo::kPalette_Color, alpha, 8);
-    return std::unique_ptr<SkCodec>(new SkGifCodec(std::move(encodedInfo), reader.release()));
-}
-
-bool SkGifCodec::onRewind() {
-    fReader->clearDecodeState();
-    return true;
-}
-
-SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
-    : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
-    , fReader(reader)
-    , fTmpBuffer(nullptr)
-    , fSwizzler(nullptr)
-    , fCurrColorTable(nullptr)
-    , fCurrColorTableIsReal(false)
-    , fFilledBackground(false)
-    , fFirstCallToIncrementalDecode(false)
-    , fDst(nullptr)
-    , fDstRowBytes(0)
-    , fRowsDecoded(0)
-{
-    reader->setClient(this);
-}
-
-int SkGifCodec::onGetFrameCount() {
-    fReader->parse(SkGifImageReader::SkGIFFrameCountQuery);
-    return fReader->imagesCount();
-}
-
-bool SkGifCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
-    if (i >= fReader->imagesCount()) {
-        return false;
-    }
-
-    const SkGIFFrameContext* frameContext = fReader->frameContext(i);
-    SkASSERT(frameContext->reachedStartOfData());
-
-    if (frameInfo) {
-        frameInfo->fDuration = frameContext->getDuration();
-        frameInfo->fRequiredFrame = frameContext->getRequiredFrame();
-        frameInfo->fFullyReceived = frameContext->isComplete();
-        frameInfo->fAlphaType = frameContext->hasAlpha() ? kUnpremul_SkAlphaType
-                                                         : kOpaque_SkAlphaType;
-        frameInfo->fDisposalMethod = frameContext->getDisposalMethod();
-    }
-    return true;
-}
-
-int SkGifCodec::onGetRepetitionCount() {
-    fReader->parse(SkGifImageReader::SkGIFLoopCountQuery);
-    return fReader->loopCount();
-}
-
-static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
-
-void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
-    SkColorType colorTableColorType = dstInfo.colorType();
-    if (this->colorXform()) {
-        colorTableColorType = kXformSrcColorType;
-    }
-
-    sk_sp<SkColorTable> currColorTable = fReader->getColorTable(colorTableColorType, frameIndex);
-    fCurrColorTableIsReal = static_cast<bool>(currColorTable);
-    if (!fCurrColorTableIsReal) {
-        // This is possible for an empty frame. Create a dummy with one value (transparent).
-        SkPMColor color = SK_ColorTRANSPARENT;
-        fCurrColorTable.reset(new SkColorTable(&color, 1));
-    } else if (this->colorXform() && !this->xformOnDecode()) {
-        SkPMColor dstColors[256];
-        this->applyColorXform(dstColors, currColorTable->readColors(),
-                              currColorTable->count());
-        fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
-    } else {
-        fCurrColorTable = std::move(currColorTable);
-    }
-}
-
-
-SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, const Options& opts) {
-    if (opts.fSubset) {
-        return gif_error("Subsets not supported.\n", kUnimplemented);
-    }
-
-    const int frameIndex = opts.fFrameIndex;
-    if (frameIndex > 0 && kRGB_565_SkColorType == dstInfo.colorType()) {
-        // FIXME: In theory, we might be able to support this, but it's not clear that it
-        // is necessary (Chromium does not decode to 565, and Android does not decode
-        // frames beyond the first). Disabling it because it is somewhat difficult:
-        // - If there is a transparent pixel, and this frame draws on top of another frame
-        //   (if the frame is independent with a transparent pixel, we should not decode to
-        //   565 anyway, since it is not opaque), we need to skip drawing the transparent
-        //   pixels (see writeTransparentPixels in haveDecodedRow). We currently do this by
-        //   first swizzling into temporary memory, then copying into the destination. (We
-        //   let the swizzler handle it first because it may need to sample.) After
-        //   swizzling to 565, we do not know which pixels in our temporary memory
-        //   correspond to the transparent pixel, so we do not know what to skip. We could
-        //   special case the non-sampled case (no need to swizzle), but as this is
-        //   currently unused we can just not support it.
-        return gif_error("Cannot decode multiframe gif (except frame 0) as 565.\n",
-                         kInvalidConversion);
-    }
-
-    const auto* frame = fReader->frameContext(frameIndex);
-    SkASSERT(frame);
-    if (0 == frameIndex) {
-        // SkCodec does not have a way to just parse through frame 0, so we
-        // have to do so manually, here.
-        fReader->parse((SkGifImageReader::SkGIFParseQuery) 0);
-        if (!frame->reachedStartOfData()) {
-            // We have parsed enough to know that there is a color map, but cannot
-            // parse the map itself yet. Exit now, so we do not build an incorrect
-            // table.
-            return gif_error("color map not available yet\n", kIncompleteInput);
-        }
-    } else {
-        // Parsing happened in SkCodec::getPixels.
-        SkASSERT(frameIndex < fReader->imagesCount());
-        SkASSERT(frame->reachedStartOfData());
-    }
-
-    if (this->xformOnDecode()) {
-        fXformBuffer.reset(new uint32_t[dstInfo.width()]);
-        sk_bzero(fXformBuffer.get(), dstInfo.width() * sizeof(uint32_t));
-    }
-
-    fTmpBuffer.reset(new uint8_t[dstInfo.minRowBytes()]);
-
-    this->initializeColorTable(dstInfo, frameIndex);
-    this->initializeSwizzler(dstInfo, frameIndex);
-
-    SkASSERT(fCurrColorTable);
-    return kSuccess;
-}
-
-void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex) {
-    const SkGIFFrameContext* frame = fReader->frameContext(frameIndex);
-    // This is only called by prepareToDecode, which ensures frameIndex is in range.
-    SkASSERT(frame);
-
-    const int xBegin = frame->xOffset();
-    const int xEnd = std::min(frame->frameRect().right(), fReader->screenWidth());
-
-    // CreateSwizzler only reads left and right of the frame. We cannot use the frame's raw
-    // frameRect, since it might extend beyond the edge of the frame.
-    SkIRect swizzleRect = SkIRect::MakeLTRB(xBegin, 0, xEnd, 0);
-
-    SkImageInfo swizzlerInfo = dstInfo;
-    if (this->colorXform()) {
-        swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
-        if (kPremul_SkAlphaType == dstInfo.alphaType()) {
-            swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
-        }
-    }
-
-    // The default Options should be fine:
-    // - we'll ignore if the memory is zero initialized - unless we're the first frame, this won't
-    //   matter anyway.
-    // - subsets are not supported for gif
-    // - the swizzler does not need to know about the frame.
-    // We may not be able to use the real Options anyway, since getPixels does not store it (due to
-    // a bug).
-    fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), fCurrColorTable->readColors(),
-                                 swizzlerInfo, Options(), &swizzleRect);
-    SkASSERT(fSwizzler.get());
-}
-
-/*
- * Initiates the gif decode
- */
-SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
-                                        void* pixels, size_t dstRowBytes,
-                                        const Options& opts,
-                                        int* rowsDecoded) {
-    Result result = this->prepareToDecode(dstInfo, opts);
-    switch (result) {
-        case kSuccess:
-            break;
-        case kIncompleteInput:
-            // onStartIncrementalDecode treats this as incomplete, since it may
-            // provide more data later, but in this case, no more data will be
-            // provided, and there is nothing to draw. We also cannot return
-            // kIncompleteInput, which will make SkCodec attempt to fill
-            // remaining rows, but that requires an SkSwizzler, which we have
-            // not created.
-            return kInvalidInput;
-        default:
-            return result;
-    }
-
-    if (dstInfo.dimensions() != this->dimensions()) {
-        return gif_error("Scaling not supported.\n", kInvalidScale);
-    }
-
-    fDst = pixels;
-    fDstRowBytes = dstRowBytes;
-
-    return this->decodeFrame(true, opts, rowsDecoded);
-}
-
-SkCodec::Result SkGifCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
-                                                     void* pixels, size_t dstRowBytes,
-                                                     const SkCodec::Options& opts) {
-    Result result = this->prepareToDecode(dstInfo, opts);
-    if (result != kSuccess) {
-        return result;
-    }
-
-    fDst = pixels;
-    fDstRowBytes = dstRowBytes;
-
-    fFirstCallToIncrementalDecode = true;
-
-    return kSuccess;
-}
-
-SkCodec::Result SkGifCodec::onIncrementalDecode(int* rowsDecoded) {
-    // It is possible the client has appended more data. Parse, if needed.
-    const auto& options = this->options();
-    const int frameIndex = options.fFrameIndex;
-    fReader->parse((SkGifImageReader::SkGIFParseQuery) frameIndex);
-
-    const bool firstCallToIncrementalDecode = fFirstCallToIncrementalDecode;
-    fFirstCallToIncrementalDecode = false;
-    return this->decodeFrame(firstCallToIncrementalDecode, options, rowsDecoded);
-}
-
-SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts, int* rowsDecoded) {
-    const SkImageInfo& dstInfo = this->dstInfo();
-    const int scaledHeight = get_scaled_dimension(dstInfo.height(), fSwizzler->sampleY());
-
-    const int frameIndex = opts.fFrameIndex;
-    SkASSERT(frameIndex < fReader->imagesCount());
-    const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
-    if (firstAttempt) {
-        // rowsDecoded reports how many rows have been initialized, so a layer above
-        // can fill the rest. In some cases, we fill the background before decoding
-        // (or it is already filled for us), so we report rowsDecoded to be the full
-        // height.
-        bool filledBackground = false;
-        if (frameContext->getRequiredFrame() == kNoFrame) {
-            // We may need to clear to transparent for one of the following reasons:
-            // - The frameRect does not cover the full bounds. haveDecodedRow will
-            //   only draw inside the frameRect, so we need to clear the rest.
-            // - The frame is interlaced. There is no obvious way to fill
-            //   afterwards for an incomplete image. (FIXME: Does the first pass
-            //   cover all rows? If so, we do not have to fill here.)
-            // - There is no color table for this frame. In that case will not
-            //   draw anything, so we need to fill.
-            if (frameContext->frameRect() != this->bounds()
-                    || frameContext->interlaced() || !fCurrColorTableIsReal) {
-                auto fillInfo = dstInfo.makeWH(fSwizzler->fillWidth(), scaledHeight);
-                SkSampler::Fill(fillInfo, fDst, fDstRowBytes, opts.fZeroInitialized);
-                filledBackground = true;
-            }
-        } else {
-            // Not independent.
-            // SkCodec ensured that the prior frame has been decoded.
-            filledBackground = true;
-        }
-
-        fFilledBackground = filledBackground;
-        if (filledBackground) {
-            // Report the full (scaled) height, since the client will never need to fill.
-            fRowsDecoded = scaledHeight;
-        } else {
-            // This will be updated by haveDecodedRow.
-            fRowsDecoded = 0;
-        }
-    }
-
-    if (!fCurrColorTableIsReal) {
-        // Nothing to draw this frame.
-        return kSuccess;
-    }
-
-    bool frameDecoded = false;
-    const bool fatalError = !fReader->decode(frameIndex, &frameDecoded);
-    if (fatalError || !frameDecoded || fRowsDecoded != scaledHeight) {
-        if (rowsDecoded) {
-            *rowsDecoded = fRowsDecoded;
-        }
-        if (fatalError) {
-            return kErrorInInput;
-        }
-        return kIncompleteInput;
-    }
-
-    return kSuccess;
-}
-
-void SkGifCodec::applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint8_t* src) const {
-    if (this->xformOnDecode()) {
-        SkASSERT(this->colorXform());
-        fSwizzler->swizzle(fXformBuffer.get(), src);
-
-        const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
-        this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
-    } else {
-        fSwizzler->swizzle(dst, src);
-    }
-}
-
-template <typename T>
-static void blend_line(void* dstAsVoid, const void* srcAsVoid, int width) {
-    T*       dst = reinterpret_cast<T*>(dstAsVoid);
-    const T* src = reinterpret_cast<const T*>(srcAsVoid);
-    while (width --> 0) {
-        if (*src != 0) {   // GIF pixels are either transparent (== 0) or opaque (!= 0).
-            *dst = *src;
-        }
-        src++;
-        dst++;
-    }
-}
-
-void SkGifCodec::haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
-                                int rowNumber, int repeatCount, bool writeTransparentPixels)
-{
-    const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
-    // The pixel data and coordinates supplied to us are relative to the frame's
-    // origin within the entire image size, i.e.
-    // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
-    // that width == (size().width() - frameContext->xOffset), so
-    // we must ensure we don't run off the end of either the source data or the
-    // row's X-coordinates.
-    const int width = frameContext->width();
-    const int xBegin = frameContext->xOffset();
-    const int yBegin = frameContext->yOffset() + rowNumber;
-    const int xEnd = std::min(xBegin + width, this->dimensions().width());
-    const int yEnd = std::min(yBegin + rowNumber + repeatCount, this->dimensions().height());
-    // FIXME: No need to make the checks on width/xBegin/xEnd for every row. We could instead do
-    // this once in prepareToDecode.
-    if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
-        return;
-
-    // yBegin is the first row in the non-sampled image. dstRow will be the row in the output,
-    // after potentially scaling it.
-    int dstRow = yBegin;
-
-    const int sampleY = fSwizzler->sampleY();
-    if (sampleY > 1) {
-        // Check to see whether this row or one that falls in the repeatCount is needed in the
-        // output.
-        bool foundNecessaryRow = false;
-        for (int i = 0; i < repeatCount; i++) {
-            const int potentialRow = yBegin + i;
-            if (fSwizzler->rowNeeded(potentialRow)) {
-                dstRow = potentialRow / sampleY;
-                const int scaledHeight = get_scaled_dimension(this->dstInfo().height(), sampleY);
-                if (dstRow >= scaledHeight) {
-                    return;
-                }
-
-                foundNecessaryRow = true;
-                repeatCount -= i;
-
-                repeatCount = (repeatCount - 1) / sampleY + 1;
-
-                // Make sure the repeatCount does not take us beyond the end of the dst
-                if (dstRow + repeatCount > scaledHeight) {
-                    repeatCount = scaledHeight - dstRow;
-                    SkASSERT(repeatCount >= 1);
-                }
-                break;
-            }
-        }
-
-        if (!foundNecessaryRow) {
-            return;
-        }
-    } else {
-        // Make sure the repeatCount does not take us beyond the end of the dst
-        SkASSERT(this->dstInfo().height() >= yBegin);
-        repeatCount = SkTMin(repeatCount, this->dstInfo().height() - yBegin);
-    }
-
-    if (!fFilledBackground) {
-        // At this point, we are definitely going to write the row, so count it towards the number
-        // of rows decoded.
-        // We do not consider the repeatCount, which only happens for interlaced, in which case we
-        // have already set fRowsDecoded to the proper value (reflecting that we have filled the
-        // background).
-        fRowsDecoded++;
-    }
-
-    // decodeFrame will early exit if this is false, so this method will not be
-    // called.
-    SkASSERT(fCurrColorTableIsReal);
-
-    // The swizzler takes care of offsetting into the dst width-wise.
-    void* dstLine = SkTAddOffset<void>(fDst, dstRow * fDstRowBytes);
-
-    // We may or may not need to write transparent pixels to the buffer.
-    // If we're compositing against a previous image, it's wrong, but if
-    // we're decoding an interlaced gif and displaying it "Haeberli"-style,
-    // we must write these for passes beyond the first, or the initial passes
-    // will "show through" the later ones.
-    const auto dstInfo = this->dstInfo();
-    if (writeTransparentPixels) {
-        this->applyXformRow(dstInfo, dstLine, rowBegin);
-    } else {
-        this->applyXformRow(dstInfo, fTmpBuffer.get(), rowBegin);
-
-        size_t offsetBytes = fSwizzler->swizzleOffsetBytes();
-        if (dstInfo.colorType() == kRGBA_F16_SkColorType) {
-            // Account for the fact that post-swizzling we converted to F16,
-            // which is twice as wide.
-            offsetBytes *= 2;
-        }
-        const void* src = SkTAddOffset<void>(fTmpBuffer.get(), offsetBytes);
-        void*       dst = SkTAddOffset<void>(dstLine, offsetBytes);
-
-        switch (dstInfo.colorType()) {
-            case kBGRA_8888_SkColorType:
-            case kRGBA_8888_SkColorType:
-                blend_line<uint32_t>(dst, src, fSwizzler->swizzleWidth());
-                break;
-            case kRGBA_F16_SkColorType:
-                blend_line<uint64_t>(dst, src, fSwizzler->swizzleWidth());
-                break;
-            default:
-                SkASSERT(false);
-                return;
-        }
-    }
-
-    // Tell the frame to copy the row data if need be.
-    if (repeatCount > 1) {
-        const size_t bytesPerPixel = this->dstInfo().bytesPerPixel();
-        const size_t bytesToCopy = fSwizzler->swizzleWidth() * bytesPerPixel;
-        void* copiedLine = SkTAddOffset<void>(dstLine, fSwizzler->swizzleOffsetBytes());
-        void* dst = copiedLine;
-        for (int i = 1; i < repeatCount; i++) {
-            dst = SkTAddOffset<void>(dst, fDstRowBytes);
-            memcpy(dst, copiedLine, bytesToCopy);
-        }
-    }
-}
diff --git a/src/codec/SkGifCodec.h b/src/codec/SkGifCodec.h
deleted file mode 100644
index 1a825fa..0000000
--- a/src/codec/SkGifCodec.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkGifCodec_DEFINED
-#define SkGifCodec_DEFINED
-
-#include "include/codec/SkCodec.h"
-#include "include/codec/SkCodecAnimation.h"
-#include "include/core/SkColorSpace.h"
-#include "include/core/SkImageInfo.h"
-#include "src/codec/SkColorTable.h"
-#include "src/codec/SkSwizzler.h"
-
-#include "third_party/gif/SkGifImageReader.h"
-
-/*
- *
- * This class implements the decoding for gif images
- *
- */
-class SkGifCodec : public SkCodec {
-public:
-    static bool IsGif(const void*, size_t);
-
-    /*
-     * Assumes IsGif was called and returned true
-     * Reads enough of the stream to determine the image format
-     */
-    static std::unique_ptr<SkCodec> MakeFromStream(std::unique_ptr<SkStream>, Result*);
-
-    // Callback for SkGifImageReader when a row is available.
-    void haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
-                        int rowNumber, int repeatCount, bool writeTransparentPixels);
-protected:
-    /*
-     * Performs the full gif decode
-     */
-    Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&,
-            int*) override;
-
-    SkEncodedImageFormat onGetEncodedFormat() const override {
-        return SkEncodedImageFormat::kGIF;
-    }
-
-    bool onRewind() override;
-
-    int onGetFrameCount() override;
-    bool onGetFrameInfo(int, FrameInfo*) const override;
-    int onGetRepetitionCount() override;
-
-    Result onStartIncrementalDecode(const SkImageInfo& /*dstInfo*/, void*, size_t,
-            const SkCodec::Options&) override;
-
-    Result onIncrementalDecode(int*) override;
-
-    const SkFrameHolder* getFrameHolder() const override {
-        return fReader.get();
-    }
-
-private:
-
-    /*
-     * Initializes the color table that we will use for decoding.
-     *
-     * @param dstInfo         Contains the requested dst color type.
-     * @param frameIndex      Frame whose color table to use.
-     */
-    void initializeColorTable(const SkImageInfo& dstInfo, int frameIndex);
-
-   /*
-    * Does necessary setup, including setting up the color table and swizzler.
-    */
-    Result prepareToDecode(const SkImageInfo& dstInfo, const Options& opts);
-
-    /*
-     * Initializes the swizzler.
-     *
-     * @param dstInfo    Output image information.  Dimensions may have been
-     *                   adjusted if the image frame size does not match the size
-     *                   indicated in the header.
-     * @param frameIndex Which frame we are decoding. This determines the frameRect
-     *                   to use.
-     */
-    void initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex);
-
-    SkSampler* getSampler(bool createIfNecessary) override {
-        SkASSERT(fSwizzler);
-        return fSwizzler.get();
-    }
-
-    /*
-     * Recursive function to decode a frame.
-     *
-     * @param firstAttempt Whether this is the first call to decodeFrame since
-     *                     starting. e.g. true in onGetPixels, and true in the
-     *                     first call to onIncrementalDecode after calling
-     *                     onStartIncrementalDecode.
-     *                     When true, this method may have to initialize the
-     *                     frame, for example by filling or decoding the prior
-     *                     frame.
-     * @param opts         Options for decoding. May be different from
-     *                     this->options() for decoding prior frames. Specifies
-     *                     the frame to decode and whether the prior frame has
-     *                     already been decoded to fDst. If not, and the frame
-     *                     is not independent, this method will recursively
-     *                     decode the frame it depends on.
-     * @param rowsDecoded  Out-parameter to report the total number of rows
-     *                     that have been decoded (or at least written to, if
-     *                     it had to fill), including rows decoded by prior
-     *                     calls to onIncrementalDecode.
-     * @return             kSuccess if the frame is complete, kIncompleteInput
-     *                     otherwise.
-     */
-    Result decodeFrame(bool firstAttempt, const Options& opts, int* rowsDecoded);
-
-    /*
-     *  Swizzles and color xforms (if necessary) into dst.
-     */
-    void applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint8_t* src) const;
-
-    /*
-     * Creates an instance of the decoder
-     * Called only by NewFromStream
-     * Takes ownership of the SkGifImageReader
-     */
-    SkGifCodec(SkEncodedInfo&&, SkGifImageReader*);
-
-    std::unique_ptr<SkGifImageReader>   fReader;
-    std::unique_ptr<uint8_t[]>          fTmpBuffer;
-    std::unique_ptr<SkSwizzler>         fSwizzler;
-    sk_sp<SkColorTable>                 fCurrColorTable;
-    // We may create a dummy table if there is not a Map in the input data. In
-    // that case, we set this value to false, and we can skip a lot of decoding
-    // work (which would not be meaningful anyway). We create a "fake"/"dummy"
-    // one in that case, so the client and the swizzler have something to draw.
-    bool                                fCurrColorTableIsReal;
-    // Whether the background was filled.
-    bool                                fFilledBackground;
-    // True on the first call to onIncrementalDecode. This value is passed to
-    // decodeFrame.
-    bool                                fFirstCallToIncrementalDecode;
-
-    void*                               fDst;
-    size_t                              fDstRowBytes;
-
-    // Updated inside haveDecodedRow when rows are decoded, unless we filled
-    // the background, in which case it is set once and left alone.
-    int                                 fRowsDecoded;
-    std::unique_ptr<uint32_t[]>         fXformBuffer;
-
-    typedef SkCodec INHERITED;
-};
-#endif  // SkGifCodec_DEFINED
diff --git a/src/codec/SkWuffsCodec.cpp b/src/codec/SkWuffsCodec.cpp
index f7ada1f..ac62b20 100644
--- a/src/codec/SkWuffsCodec.cpp
+++ b/src/codec/SkWuffsCodec.cpp
@@ -83,6 +83,55 @@
     }
 }
 
+static SkAlphaType to_alpha_type(bool opaque) {
+    return opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+}
+
+static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder*       decoder,
+                                                     wuffs_base__image_config* imgcfg,
+                                                     wuffs_base__io_buffer*    b,
+                                                     SkStream*                 s) {
+    // Calling decoder->initialize will memset it to zero.
+    const char* status = decoder->initialize(sizeof__wuffs_gif__decoder(), WUFFS_VERSION, 0);
+    if (status != nullptr) {
+        SkCodecPrintf("initialize: %s", status);
+        return SkCodec::kInternalError;
+    }
+    while (true) {
+        status = decoder->decode_image_config(imgcfg, b);
+        if (status == nullptr) {
+            break;
+        } else if (status != wuffs_base__suspension__short_read) {
+            SkCodecPrintf("decode_image_config: %s", status);
+            return SkCodec::kErrorInInput;
+        } else if (!fill_buffer(b, s)) {
+            return SkCodec::kIncompleteInput;
+        }
+    }
+
+    // A GIF image's natural color model is indexed color: 1 byte per pixel,
+    // indexing a 256-element palette.
+    //
+    // For Skia, we override that to decode to 4 bytes per pixel, BGRA or RGBA.
+    wuffs_base__pixel_format pixfmt = 0;
+    switch (kN32_SkColorType) {
+        case kBGRA_8888_SkColorType:
+            pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
+            break;
+        case kRGBA_8888_SkColorType:
+            pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
+            break;
+        default:
+            return SkCodec::kInternalError;
+    }
+    if (imgcfg) {
+        imgcfg->pixcfg.set(pixfmt, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, imgcfg->pixcfg.width(),
+                           imgcfg->pixcfg.height());
+    }
+
+    return SkCodec::kSuccess;
+}
+
 // -------------------------------- Class definitions
 
 class SkWuffsCodec;
@@ -129,6 +178,7 @@
                  std::unique_ptr<SkStream>                               stream,
                  std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
                  std::unique_ptr<uint8_t, decltype(&sk_free)>            pixbuf_ptr,
+                 bool                                                    pixbuf_zeroed,
                  std::unique_ptr<uint8_t, decltype(&sk_free)>            workbuf_ptr,
                  size_t                                                  workbuf_len,
                  wuffs_base__image_config                                imgcfg,
@@ -138,6 +188,37 @@
     const SkWuffsFrame* frame(int i) const;
 
 private:
+    // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount
+    // while in an incremental decode (after onStartIncrementalDecode returns
+    // and before the rest of the image is decoded). Some Skia users expect
+    // getFrameCount to increase, and the SkStream to advance, when given more
+    // data.
+    //
+    // On the other hand, while in an incremental decode, the underlying Wuffs
+    // object is suspended in a coroutine. To keep its internal proof-of-safety
+    // invariants consistent, there's only two things you can safely do with a
+    // suspended Wuffs object: resume the coroutine, or reset all state (memset
+    // to zero and start again).
+    //
+    // The Wuffs API provides a limited, optional form of seeking, to the start
+    // of an animation frame's data, but does not provide arbitrary save and
+    // load of its internal state whilst in the middle of an animation frame.
+    //
+    // SkWuffsCodec therefore uses two Wuffs decoders: a primary decoder
+    // (kIncrDecode) to support startIncrementalDecode / incrementalDecode, and
+    // a secondary decoder (kFrameCount) to support getFrameCount. The two
+    // decoders' states can change independently.
+    //
+    // As of Wuffs version 0.2, both of these decoders have the same type. A
+    // future Wuffs version might let us use a different type for kFrameCount,
+    // one that is much lighter weight (in terms of memory requirements), as it
+    // doesn't have to handle decompressing pixel data.
+    enum WhichDecoder {
+        kIncrDecode,
+        kFrameCount,
+        kNumDecoders,
+    };
+
     // SkCodec overrides.
     SkEncodedImageFormat onGetEncodedFormat() const override;
     Result onGetPixels(const SkImageInfo&, void*, size_t, const Options&, int*) override;
@@ -151,46 +232,55 @@
     bool                 onGetFrameInfo(int, FrameInfo*) const override;
     int                  onGetRepetitionCount() override;
 
-    void   readFrames();
-    Result seekFrame(int frameIndex);
+    Result seekFrame(WhichDecoder which, int frameIndex);
 
-    Result      resetDecoder();
-    const char* decodeFrameConfig();
-    const char* decodeFrame();
-    void        updateNumFullyReceivedFrames();
+    void   onGetFrameCountInternal();
+    Result onIncrementalDecodeInternal(int* rowsDecoded);
 
-    SkWuffsFrameHolder                                      fFrameHolder;
-    std::unique_ptr<SkStream>                               fStream;
-    std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> fDecoder;
-    std::unique_ptr<uint8_t, decltype(&sk_free)>            fPixbufPtr;
-    std::unique_ptr<uint8_t, decltype(&sk_free)>            fWorkbufPtr;
-    size_t                                                  fWorkbufLen;
+    Result      resetDecoder(WhichDecoder which);
+    const char* decodeFrameConfig(WhichDecoder which);
+    const char* decodeFrame(WhichDecoder which);
+    void        updateNumFullyReceivedFrames(WhichDecoder which);
+
+    SkWuffsFrameHolder                           fFrameHolder;
+    std::unique_ptr<SkStream>                    fStream;
+    std::unique_ptr<uint8_t, decltype(&sk_free)> fPixbufPtr;
+    std::unique_ptr<uint8_t, decltype(&sk_free)> fWorkbufPtr;
+    size_t                                       fWorkbufLen;
+
+    std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> fDecoders[WhichDecoder::kNumDecoders];
 
     const uint64_t           fFirstFrameIOPosition;
-    wuffs_base__frame_config fFrameConfig;
+    wuffs_base__frame_config fFrameConfigs[WhichDecoder::kNumDecoders];
     wuffs_base__pixel_buffer fPixelBuffer;
     wuffs_base__io_buffer    fIOBuffer;
 
     // Incremental decoding state.
     uint8_t* fIncrDecDst;
+    uint64_t fIncrDecReaderIOPosition;
     size_t   fIncrDecRowBytes;
     bool     fFirstCallToIncrementalDecode;
 
+    uint64_t                  fFrameCountReaderIOPosition;
     uint64_t                  fNumFullyReceivedFrames;
     std::vector<SkWuffsFrame> fFrames;
     bool                      fFramesComplete;
 
-    // If calling an fDecoder method returns an incomplete status, then
-    // fDecoder is suspended in a coroutine (i.e. waiting on I/O or halted on a
-    // non-recoverable error). To keep its internal proof-of-safety invariants
-    // consistent, there's only two things you can safely do with a suspended
-    // Wuffs object: resume the coroutine, or reset all state (memset to zero
-    // and start again).
+    // True if fPixelBuffer's contents are known to be already zeroed. This is
+    // conservative, and may be false even if the buffer is zeroed.
+    bool fPixbufZeroed;
+
+    // If calling an fDecoders[which] method returns an incomplete status, then
+    // fDecoders[which] is suspended in a coroutine (i.e. waiting on I/O or
+    // halted on a non-recoverable error). To keep its internal proof-of-safety
+    // invariants consistent, there's only two things you can safely do with a
+    // suspended Wuffs object: resume the coroutine, or reset all state (memset
+    // to zero and start again).
     //
-    // If fDecoderIsSuspended, and we aren't sure that we're going to resume
-    // the coroutine, then we will need to call this->resetDecoder before
-    // calling other fDecoder methods.
-    bool fDecoderIsSuspended;
+    // If fDecoderIsSuspended[which], and we aren't sure that we're going to
+    // resume the coroutine, then we will need to call this->resetDecoder
+    // before calling other fDecoders[which] methods.
+    bool fDecoderIsSuspended[WhichDecoder::kNumDecoders];
 
     uint8_t fBuffer[SK_WUFFS_CODEC_BUFFER_SIZE];
 
@@ -247,6 +337,7 @@
                            std::unique_ptr<SkStream>                               stream,
                            std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> dec,
                            std::unique_ptr<uint8_t, decltype(&sk_free)>            pixbuf_ptr,
+                           bool                                                    pixbuf_zeroed,
                            std::unique_ptr<uint8_t, decltype(&sk_free)>            workbuf_ptr,
                            size_t                                                  workbuf_len,
                            wuffs_base__image_config                                imgcfg,
@@ -260,20 +351,32 @@
                 nullptr),
       fFrameHolder(),
       fStream(std::move(stream)),
-      fDecoder(std::move(dec)),
       fPixbufPtr(std::move(pixbuf_ptr)),
       fWorkbufPtr(std::move(workbuf_ptr)),
       fWorkbufLen(workbuf_len),
+      fDecoders{
+          std::move(dec),
+          std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)>(nullptr, sk_free),
+      },
       fFirstFrameIOPosition(imgcfg.first_frame_io_position()),
-      fFrameConfig(wuffs_base__null_frame_config()),
+      fFrameConfigs{
+          wuffs_base__null_frame_config(),
+          wuffs_base__null_frame_config(),
+      },
       fPixelBuffer(pixbuf),
       fIOBuffer(wuffs_base__empty_io_buffer()),
       fIncrDecDst(nullptr),
+      fIncrDecReaderIOPosition(0),
       fIncrDecRowBytes(0),
       fFirstCallToIncrementalDecode(false),
+      fFrameCountReaderIOPosition(0),
       fNumFullyReceivedFrames(0),
       fFramesComplete(false),
-      fDecoderIsSuspended(false) {
+      fPixbufZeroed(pixbuf_zeroed),
+      fDecoderIsSuspended{
+          false,
+          false,
+      } {
     fFrameHolder.init(this, imgcfg.pixcfg.width(), imgcfg.pixcfg.height());
 
     // Initialize fIOBuffer's fields, copying any outstanding data from iobuf to
@@ -325,12 +428,12 @@
     if (options.fFrameIndex > 0 && SkColorTypeIsAlwaysOpaque(dstInfo.colorType())) {
         return SkCodec::kInvalidConversion;
     }
-    SkCodec::Result result = this->seekFrame(options.fFrameIndex);
+    SkCodec::Result result = this->seekFrame(WhichDecoder::kIncrDecode, options.fFrameIndex);
     if (result != SkCodec::kSuccess) {
         return result;
     }
 
-    const char* status = this->decodeFrameConfig();
+    const char* status = this->decodeFrameConfig(WhichDecoder::kIncrDecode);
     if (status == wuffs_base__suspension__short_read) {
         return SkCodec::kIncompleteInput;
     } else if (status != nullptr) {
@@ -346,33 +449,71 @@
     size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
 
     // Zero-initialize Wuffs' buffer covering the frame rect.
-    wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds();
-    wuffs_base__table_u8    pixels = fPixelBuffer.plane(0);
-    for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) {
-        sk_bzero(pixels.ptr + (y * pixels.stride) + (frame_rect.min_incl_x * src_bytes_per_pixel),
-                 frame_rect.width() * src_bytes_per_pixel);
+    if (!fPixbufZeroed) {
+        wuffs_base__rect_ie_u32 frame_rect = fFrameConfigs[WhichDecoder::kIncrDecode].bounds();
+        wuffs_base__table_u8    pixels = fPixelBuffer.plane(0);
+
+        uint8_t* ptr = pixels.ptr + (frame_rect.min_incl_y * pixels.stride) +
+                       (frame_rect.min_incl_x * src_bytes_per_pixel);
+        size_t len = frame_rect.width() * src_bytes_per_pixel;
+
+        // As an optimization, issue a single sk_bzero call, if possible.
+        // Otherwise, zero out each row separately.
+        if ((len == pixels.stride) && (frame_rect.min_incl_y < frame_rect.max_excl_y)) {
+            sk_bzero(ptr, len * (frame_rect.max_excl_y - frame_rect.min_incl_y));
+        } else {
+            for (uint32_t y = frame_rect.min_incl_y; y < frame_rect.max_excl_y; y++) {
+                sk_bzero(ptr, len);
+                ptr += pixels.stride;
+            }
+        }
     }
+    // The buffer is zeroed now, but this onStartIncrementalDecode call will
+    // almost certainly be followed by some onIncrementalDecode calls that can
+    // modify fPixelBuffer's contents. We set fPixbufZeroed to false so that
+    // the next onStartIncrementalDecode call will zero-initialize the buffer.
+    fPixbufZeroed = false;
 
     fIncrDecDst = static_cast<uint8_t*>(dst);
+    fIncrDecReaderIOPosition = fIOBuffer.reader_io_position();
     fIncrDecRowBytes = rowBytes;
     fFirstCallToIncrementalDecode = true;
     return SkCodec::kSuccess;
 }
 
-static SkAlphaType to_alpha_type(bool opaque) {
-    return opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
-}
-
 SkCodec::Result SkWuffsCodec::onIncrementalDecode(int* rowsDecoded) {
     if (!fIncrDecDst) {
         return SkCodec::kInternalError;
     }
 
+    // If multiple SkCodec::incrementalDecode calls are made consecutively (or
+    // if SkCodec::incrementalDecode is called immediately after
+    // SkCodec::startIncrementalDecode), then this seek should be a no-op.
+    // However, it is possible to interleave SkCodec::getFrameCount calls in
+    // between SkCodec::incrementalDecode calls, and those other calls may
+    // advance the stream. This seek restores the stream to where the last
+    // SkCodec::startIncrementalDecode or SkCodec::incrementalDecode stopped.
+    if (!seek_buffer(&fIOBuffer, fStream.get(), fIncrDecReaderIOPosition)) {
+        return SkCodec::kInternalError;
+    }
+
+    SkCodec::Result result = this->onIncrementalDecodeInternal(rowsDecoded);
+    if (result == SkCodec::kSuccess) {
+        fIncrDecDst = nullptr;
+        fIncrDecReaderIOPosition = 0;
+        fIncrDecRowBytes = 0;
+    } else {
+        fIncrDecReaderIOPosition = fIOBuffer.reader_io_position();
+    }
+    return result;
+}
+
+SkCodec::Result SkWuffsCodec::onIncrementalDecodeInternal(int* rowsDecoded) {
     SkCodec::Result result = SkCodec::kSuccess;
-    const char*     status = this->decodeFrame();
-    bool independent;
-    SkAlphaType alphaType;
-    const int index = options().fFrameIndex;
+    const char*     status = this->decodeFrame(WhichDecoder::kIncrDecode);
+    bool            independent;
+    SkAlphaType     alphaType;
+    const int       index = options().fFrameIndex;
     if (index == 0) {
         independent = true;
         alphaType = to_alpha_type(getEncodedInfo().opaque());
@@ -403,7 +544,7 @@
     }
     size_t src_bytes_per_pixel = src_bits_per_pixel / 8;
 
-    wuffs_base__rect_ie_u32 frame_rect = fFrameConfig.bounds();
+    wuffs_base__rect_ie_u32 frame_rect = fFrameConfigs[WhichDecoder::kIncrDecode].bounds();
     if (fFirstCallToIncrementalDecode) {
         if (frame_rect.width() > (SIZE_MAX / src_bytes_per_pixel)) {
             return SkCodec::kInternalError;
@@ -415,8 +556,7 @@
         // If the frame rect does not fill the output, ensure that those pixels are not
         // left uninitialized.
         if (independent && (bounds != this->bounds() || result != kSuccess)) {
-            SkSampler::Fill(dstInfo(), fIncrDecDst, fIncrDecRowBytes,
-                            options().fZeroInitialized);
+            SkSampler::Fill(dstInfo(), fIncrDecDst, fIncrDecRowBytes, options().fZeroInitialized);
         }
         fFirstCallToIncrementalDecode = false;
     } else {
@@ -436,7 +576,7 @@
     }
 
     // If the frame's dirty rect is empty, no need to swizzle.
-    wuffs_base__rect_ie_u32 dirty_rect = fDecoder->frame_dirty_rect();
+    wuffs_base__rect_ie_u32 dirty_rect = fDecoders[WhichDecoder::kIncrDecode]->frame_dirty_rect();
     if (!dirty_rect.is_empty()) {
         wuffs_base__table_u8 pixels = fPixelBuffer.plane(0);
 
@@ -446,16 +586,16 @@
         //
         // To get from the start (in the X-direction) of the image to the start
         // of the dirty_rect, we adjust s by (dirty_rect.min_incl_x * src_bytes_per_pixel).
-        uint8_t* s = pixels.ptr + (dirty_rect.min_incl_y * pixels.stride)
-                                + (dirty_rect.min_incl_x * src_bytes_per_pixel);
+        uint8_t* s = pixels.ptr + (dirty_rect.min_incl_y * pixels.stride) +
+                     (dirty_rect.min_incl_x * src_bytes_per_pixel);
 
         // Currently, this is only used for GIF, which will never have an ICC profile. When it is
         // used for other formats that might have one, we will need to transform from profiles that
         // do not have corresponding SkColorSpaces.
         SkASSERT(!getEncodedInfo().profile());
 
-        auto srcInfo = getInfo().makeWH(dirty_rect.width(), dirty_rect.height())
-                                .makeAlphaType(alphaType);
+        auto srcInfo =
+            getInfo().makeWH(dirty_rect.width(), dirty_rect.height()).makeAlphaType(alphaType);
         SkBitmap src;
         src.installPixels(srcInfo, s, pixels.stride);
         SkPaint paint;
@@ -475,52 +615,62 @@
         SkMatrix translate = SkMatrix::MakeTrans(dirty_rect.min_incl_x, dirty_rect.min_incl_y);
         draw.drawBitmap(src, translate, nullptr, paint);
     }
-
-    if (result == SkCodec::kSuccess) {
-        fIncrDecDst = nullptr;
-        fIncrDecRowBytes = 0;
-    }
     return result;
 }
 
 int SkWuffsCodec::onGetFrameCount() {
-    // It is valid, in terms of the SkCodec API, to call SkCodec::getFrameCount
-    // while in an incremental decode (after onStartIncrementalDecode returns
-    // and before onIncrementalDecode returns kSuccess).
-    //
-    // We should not advance the SkWuffsCodec' stream while doing so, even
-    // though other SkCodec implementations can return increasing values from
-    // onGetFrameCount when given more data. If we tried to do so, the
-    // subsequent resume of the incremental decode would continue reading from
-    // a different position in the I/O stream, leading to an incorrect error.
-    //
-    // Other SkCodec implementations can move the stream forward during
-    // onGetFrameCount because they assume that the stream is rewindable /
-    // seekable. For example, an alternative GIF implementation may choose to
-    // store, for each frame walked past when merely counting the number of
-    // frames, the I/O position of each of the frame's GIF data blocks. (A GIF
-    // frame's compressed data can have multiple data blocks, each at most 255
-    // bytes in length). Obviously, this can require O(numberOfFrames) extra
-    // memory to store these I/O positions. The constant factor is small, but
-    // it's still O(N), not O(1).
-    //
-    // Wuffs and SkWuffsCodec tries to minimize relying on the rewindable /
-    // seekable assumption. By design, Wuffs per se aims for O(1) memory use
-    // (after any pixel buffers are allocated) instead of O(N), and its I/O
-    // type, wuffs_base__io_buffer, is not necessarily rewindable or seekable.
-    //
-    // The Wuffs API provides a limited, optional form of seeking, to the start
-    // of an animation frame's data, but does not provide arbitrary save and
-    // load of its internal state whilst in the middle of an animation frame.
-    bool incrementalDecodeIsInProgress = fIncrDecDst != nullptr;
-
-    if (!fFramesComplete && !incrementalDecodeIsInProgress) {
-        this->readFrames();
-        this->updateNumFullyReceivedFrames();
+    if (!fFramesComplete && seek_buffer(&fIOBuffer, fStream.get(), fFrameCountReaderIOPosition)) {
+        this->onGetFrameCountInternal();
+        fFrameCountReaderIOPosition =
+            fDecoders[WhichDecoder::kFrameCount] ? fIOBuffer.reader_io_position() : 0;
     }
     return fFrames.size();
 }
 
+void SkWuffsCodec::onGetFrameCountInternal() {
+    if (!fDecoders[WhichDecoder::kFrameCount]) {
+        void* decoder_raw = sk_malloc_canfail(sizeof__wuffs_gif__decoder());
+        if (!decoder_raw) {
+            return;
+        }
+        std::unique_ptr<wuffs_gif__decoder, decltype(&sk_free)> decoder(
+            reinterpret_cast<wuffs_gif__decoder*>(decoder_raw), &sk_free);
+        reset_and_decode_image_config(decoder.get(), nullptr, &fIOBuffer, fStream.get());
+        fDecoders[WhichDecoder::kFrameCount] = std::move(decoder);
+    }
+
+    // Iterate through the frames, converting from Wuffs'
+    // wuffs_base__frame_config type to Skia's SkWuffsFrame type.
+    while (true) {
+        const char* status = this->decodeFrameConfig(WhichDecoder::kFrameCount);
+        if (status == nullptr) {
+            // No-op.
+        } else if (status == wuffs_base__warning__end_of_data) {
+            break;
+        } else {
+            return;
+        }
+
+        uint64_t i = fDecoders[WhichDecoder::kFrameCount]->num_decoded_frame_configs();
+        if (i > INT_MAX) {
+            break;
+        }
+        if ((i == 0) || (static_cast<size_t>(i - 1) != fFrames.size())) {
+            continue;
+        }
+        fFrames.emplace_back(&fFrameConfigs[WhichDecoder::kFrameCount]);
+        SkWuffsFrame* f = &fFrames[fFrames.size() - 1];
+        fFrameHolder.setAlphaAndRequiredFrame(f);
+    }
+
+    fFramesComplete = true;
+
+    // We've seen the end of the animation. There'll be no more frames, so we
+    // no longer need the kFrameCount decoder. Releasing it earlier than the
+    // SkWuffsCodec destructor might help peak memory use.
+    fDecoders[WhichDecoder::kFrameCount].reset(nullptr);
+}
+
 bool SkWuffsCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
     const SkWuffsFrame* f = this->frame(i);
     if (!f) {
@@ -537,7 +687,7 @@
     // number is how many times to play the loop. Skia's int number is how many
     // times to play the loop *after the first play*. Wuffs and Skia use 0 and
     // kRepetitionCountInfinite respectively to mean loop forever.
-    uint32_t n = fDecoder->num_animation_loops();
+    uint32_t n = fDecoders[WhichDecoder::kIncrDecode]->num_animation_loops();
     if (n == 0) {
         return SkCodec::kRepetitionCountInfinite;
     }
@@ -545,39 +695,9 @@
     return n < INT_MAX ? n : INT_MAX;
 }
 
-void SkWuffsCodec::readFrames() {
-    size_t n = fFrames.size();
-    int    i = n ? n - 1 : 0;
-    if (this->seekFrame(i) != SkCodec::kSuccess) {
-        return;
-    }
-
-    // Iterate through the frames, converting from Wuffs'
-    // wuffs_base__frame_config type to Skia's SkWuffsFrame type.
-    for (; i < INT_MAX; i++) {
-        const char* status = this->decodeFrameConfig();
-        if (status == nullptr) {
-            // No-op.
-        } else if (status == wuffs_base__warning__end_of_data) {
-            break;
-        } else {
-            return;
-        }
-
-        if (static_cast<size_t>(i) < fFrames.size()) {
-            continue;
-        }
-        fFrames.emplace_back(&fFrameConfig);
-        SkWuffsFrame* f = &fFrames[fFrames.size() - 1];
-        fFrameHolder.setAlphaAndRequiredFrame(f);
-    }
-
-    fFramesComplete = true;
-}
-
-SkCodec::Result SkWuffsCodec::seekFrame(int frameIndex) {
-    if (fDecoderIsSuspended) {
-        SkCodec::Result res = this->resetDecoder();
+SkCodec::Result SkWuffsCodec::seekFrame(WhichDecoder which, int frameIndex) {
+    if (fDecoderIsSuspended[which]) {
+        SkCodec::Result res = this->resetDecoder(which);
         if (res != SkCodec::kSuccess) {
             return res;
         }
@@ -597,7 +717,8 @@
     if (!seek_buffer(&fIOBuffer, fStream.get(), pos)) {
         return SkCodec::kInternalError;
     }
-    const char* status = fDecoder->restart_frame(frameIndex, fIOBuffer.reader_io_position());
+    const char* status =
+        fDecoders[which]->restart_frame(frameIndex, fIOBuffer.reader_io_position());
     if (status != nullptr) {
         return SkCodec::kInternalError;
     }
@@ -663,102 +784,58 @@
 // restart_frame, call decode_image_config. The io_buffer and its associated
 // stream will also need to be rewound.
 
-static SkCodec::Result reset_and_decode_image_config(wuffs_gif__decoder*       decoder,
-                                                     wuffs_base__image_config* imgcfg,
-                                                     wuffs_base__io_buffer*    b,
-                                                     SkStream*                 s) {
-    // Calling decoder->initialize will memset it to zero.
-    const char* status = decoder->initialize(sizeof__wuffs_gif__decoder(), WUFFS_VERSION, 0);
-    if (status != nullptr) {
-        SkCodecPrintf("initialize: %s", status);
-        return SkCodec::kInternalError;
-    }
-    while (true) {
-        status = decoder->decode_image_config(imgcfg, b);
-        if (status == nullptr) {
-            break;
-        } else if (status != wuffs_base__suspension__short_read) {
-            SkCodecPrintf("decode_image_config: %s", status);
-            return SkCodec::kErrorInInput;
-        } else if (!fill_buffer(b, s)) {
-            return SkCodec::kIncompleteInput;
-        }
-    }
-
-    // A GIF image's natural color model is indexed color: 1 byte per pixel,
-    // indexing a 256-element palette.
-    //
-    // For Skia, we override that to decode to 4 bytes per pixel, BGRA or RGBA.
-    wuffs_base__pixel_format pixfmt = 0;
-    switch (kN32_SkColorType) {
-        case kBGRA_8888_SkColorType:
-            pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
-            break;
-        case kRGBA_8888_SkColorType:
-            pixfmt = WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL;
-            break;
-        default:
-            return SkCodec::kInternalError;
-    }
-    if (imgcfg) {
-        imgcfg->pixcfg.set(pixfmt, WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, imgcfg->pixcfg.width(),
-                           imgcfg->pixcfg.height());
-    }
-
-    return SkCodec::kSuccess;
-}
-
-SkCodec::Result SkWuffsCodec::resetDecoder() {
+SkCodec::Result SkWuffsCodec::resetDecoder(WhichDecoder which) {
     if (!fStream->rewind()) {
         return SkCodec::kInternalError;
     }
     fIOBuffer.meta = wuffs_base__empty_io_buffer_meta();
 
     SkCodec::Result result =
-        reset_and_decode_image_config(fDecoder.get(), nullptr, &fIOBuffer, fStream.get());
+        reset_and_decode_image_config(fDecoders[which].get(), nullptr, &fIOBuffer, fStream.get());
     if (result == SkCodec::kIncompleteInput) {
         return SkCodec::kInternalError;
     } else if (result != SkCodec::kSuccess) {
         return result;
     }
 
-    fDecoderIsSuspended = false;
+    fDecoderIsSuspended[which] = false;
     return SkCodec::kSuccess;
 }
 
-const char* SkWuffsCodec::decodeFrameConfig() {
-    while (true) {
-        const char* status = fDecoder->decode_frame_config(&fFrameConfig, &fIOBuffer);
-        if ((status == wuffs_base__suspension__short_read) &&
-            fill_buffer(&fIOBuffer, fStream.get())) {
-            continue;
-        }
-        fDecoderIsSuspended = !wuffs_base__status__is_complete(status);
-        this->updateNumFullyReceivedFrames();
-        return status;
-    }
-}
-
-const char* SkWuffsCodec::decodeFrame() {
+const char* SkWuffsCodec::decodeFrameConfig(WhichDecoder which) {
     while (true) {
         const char* status =
-            fDecoder->decode_frame(&fPixelBuffer, &fIOBuffer,
-                                   wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen), NULL);
+            fDecoders[which]->decode_frame_config(&fFrameConfigs[which], &fIOBuffer);
         if ((status == wuffs_base__suspension__short_read) &&
             fill_buffer(&fIOBuffer, fStream.get())) {
             continue;
         }
-        fDecoderIsSuspended = !wuffs_base__status__is_complete(status);
-        this->updateNumFullyReceivedFrames();
+        fDecoderIsSuspended[which] = !wuffs_base__status__is_complete(status);
+        this->updateNumFullyReceivedFrames(which);
         return status;
     }
 }
 
-void SkWuffsCodec::updateNumFullyReceivedFrames() {
+const char* SkWuffsCodec::decodeFrame(WhichDecoder which) {
+    while (true) {
+        const char* status = fDecoders[which]->decode_frame(
+            &fPixelBuffer, &fIOBuffer, wuffs_base__make_slice_u8(fWorkbufPtr.get(), fWorkbufLen),
+            NULL);
+        if ((status == wuffs_base__suspension__short_read) &&
+            fill_buffer(&fIOBuffer, fStream.get())) {
+            continue;
+        }
+        fDecoderIsSuspended[which] = !wuffs_base__status__is_complete(status);
+        this->updateNumFullyReceivedFrames(which);
+        return status;
+    }
+}
+
+void SkWuffsCodec::updateNumFullyReceivedFrames(WhichDecoder which) {
     // num_decoded_frames's return value, n, can change over time, both up and
     // down, as we seek back and forth in the underlying stream.
     // fNumFullyReceivedFrames is the highest n we've seen.
-    uint64_t n = fDecoder->num_decoded_frames();
+    uint64_t n = fDecoders[which]->num_decoded_frames();
     if (fNumFullyReceivedFrames < n) {
         fNumFullyReceivedFrames = n;
     }
@@ -833,8 +910,10 @@
     std::unique_ptr<uint8_t, decltype(&sk_free)> workbuf_ptr(
         reinterpret_cast<uint8_t*>(workbuf_ptr_raw), &sk_free);
 
-    uint64_t pixbuf_len = imgcfg.pixcfg.pixbuf_len();
-    void*    pixbuf_ptr_raw = pixbuf_len <= SIZE_MAX ? sk_malloc_canfail(pixbuf_len) : nullptr;
+    constexpr int pixbuf_sk_malloc_flags = SK_MALLOC_ZERO_INITIALIZE;
+    uint64_t      pixbuf_len = imgcfg.pixcfg.pixbuf_len();
+    void*         pixbuf_ptr_raw =
+        pixbuf_len <= SIZE_MAX ? sk_malloc_flags(pixbuf_len, pixbuf_sk_malloc_flags) : nullptr;
     if (!pixbuf_ptr_raw) {
         *result = SkCodec::kInternalError;
         return nullptr;
@@ -866,5 +945,6 @@
     *result = SkCodec::kSuccess;
     return std::unique_ptr<SkCodec>(new SkWuffsCodec(
         std::move(encodedInfo), std::move(stream), std::move(decoder), std::move(pixbuf_ptr),
-        std::move(workbuf_ptr), workbuf_len, imgcfg, pixbuf, iobuf));
+        (pixbuf_sk_malloc_flags & SK_MALLOC_ZERO_INITIALIZE) != 0, std::move(workbuf_ptr),
+        workbuf_len, imgcfg, pixbuf, iobuf));
 }
diff --git a/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
index 2f39a24..c33ea95 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -14,7 +14,6 @@
 #include "src/core/SkBlitter.h"
 #include "src/core/SkRectPriv.h"
 #include "src/core/SkScan.h"
-#include "src/utils/SkUTF.h"
 #include <atomic>
 #include <utility>
 
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
old mode 100644
new mode 100755
index 31b9652..cfc2c8b
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -262,7 +262,7 @@
     SkASSERT(fPixmap.colorType() == kN32_SkColorType);
     SkASSERT(fPixmap.alphaType() == kPremul_SkAlphaType ||
              fPixmap.alphaType() == kOpaque_SkAlphaType);
-    SkASSERT(fTileModeX == fTileModeY);
+
     SkASSERT(fTileModeX != SkTileMode::kDecal);
     SkASSERT(fFilterQuality < kHigh_SkFilterQuality);
 
@@ -289,6 +289,7 @@
     if (fAlphaScale == 256
             && fFilterQuality == kNone_SkFilterQuality
             && SkTileMode::kClamp == fTileModeX
+            && SkTileMode::kClamp == fTileModeY
             && fInvMatrix.isScaleTranslate()) {
         fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc;
     } else {
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
old mode 100644
new mode 100755
index 96135ad..0e0cd6d
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -70,7 +70,7 @@
 }
 
 // A generic implementation for unfiltered scale+translate, templated on tiling method.
-template <unsigned (*tile)(SkFixed, int), bool tryDecal>
+template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), bool tryDecal>
 static void nofilter_scale(const SkBitmapProcState& s,
                            uint32_t xy[], int count, int x, int y) {
     SkASSERT(s.fInvMatrix.isScaleTranslate());
@@ -79,7 +79,7 @@
     SkFractionalInt fx;
     {
         const SkBitmapProcStateAutoMapper mapper(s, x, y);
-        *xy++ = tile(mapper.fixedY(), s.fPixmap.height() - 1);
+        *xy++ = tiley(mapper.fixedY(), s.fPixmap.height() - 1);
         fx = mapper.fractionalIntX();
     }
 
@@ -104,19 +104,19 @@
 
     // Remember, each x-coordinate is 16-bit.
     for (; count >= 2; count -= 2) {
-        *xy++ = pack_two_shorts(tile(SkFractionalIntToFixed(fx     ), maxX),
-                                tile(SkFractionalIntToFixed(fx + dx), maxX));
+        *xy++ = pack_two_shorts(tilex(SkFractionalIntToFixed(fx     ), maxX),
+                                tilex(SkFractionalIntToFixed(fx + dx), maxX));
         fx += dx+dx;
     }
 
     auto xx = (uint16_t*)xy;
     while (count --> 0) {
-        *xx++ = tile(SkFractionalIntToFixed(fx), maxX);
+        *xx++ = tilex(SkFractionalIntToFixed(fx), maxX);
         fx += dx;
     }
 }
 
-template <unsigned (*tile)(SkFixed, int)>
+template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int)>
 static void nofilter_affine(const SkBitmapProcState& s,
                             uint32_t xy[], int count, int x, int y) {
     SkASSERT(!s.fInvMatrix.hasPerspective());
@@ -131,23 +131,26 @@
         maxY = s.fPixmap.height() - 1;
 
     while (count --> 0) {
-        *xy++ = (tile(SkFractionalIntToFixed(fy), maxY) << 16)
-              | (tile(SkFractionalIntToFixed(fx), maxX)      );
+        *xy++ = (tiley(SkFractionalIntToFixed(fy), maxY) << 16)
+              | (tilex(SkFractionalIntToFixed(fx), maxX)      );
         fx += dx;
         fy += dy;
     }
 }
 
+// used when both tilex and tiley are clamp
 // Extract the high four fractional bits from fx, the lerp parameter when filtering.
-static unsigned extract_low_bits_clamp(SkFixed fx, int /*max*/) {
+static unsigned extract_low_bits_clamp_clamp(SkFixed fx, int /*max*/) {
     // If we're already scaled up to by max like clamp/decal,
     // just grab the high four fractional bits.
     return (fx >> 12) & 0xf;
 }
-static unsigned extract_low_bits_repeat_mirror(SkFixed fx, int max) {
+
+//used when one of tilex and tiley is not clamp
+static unsigned extract_low_bits_general(SkFixed fx, int max) {
     // In repeat or mirror fx is in [0,1], so scale up by max first.
     // TODO: remove the +1 here and the -1 at the call sites...
-    return extract_low_bits_clamp((fx & 0xffff) * (max+1), max);
+    return extract_low_bits_clamp_clamp((fx & 0xffff) * (max+1), max);
 }
 
 template <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)>
@@ -158,7 +161,7 @@
     return packed;
 }
 
-template <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int), bool tryDecal>
+template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int), bool tryDecal>
 static void filter_scale(const SkBitmapProcState& s,
                          uint32_t xy[], int count, int x, int y) {
     SkASSERT(s.fInvMatrix.isScaleTranslate());
@@ -170,7 +173,7 @@
         const SkBitmapProcStateAutoMapper mapper(s, x, y);
         const unsigned maxY = s.fPixmap.height() - 1;
         // compute our two Y values up front
-        *xy++ = pack<tile, extract_low_bits>(mapper.fixedY(), maxY, s.fFilterOneY);
+        *xy++ = pack<tiley, extract_low_bits>(mapper.fixedY(), maxY, s.fFilterOneY);
         // now initialize fx
         fx = mapper.fractionalIntX();
     }
@@ -190,12 +193,12 @@
     }
 
     while (count --> 0) {
-        *xy++ = pack<tile, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, s.fFilterOneX);
+        *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, s.fFilterOneX);
         fx += dx;
     }
 }
 
-template <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)>
+template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)>
 static void filter_affine(const SkBitmapProcState& s,
                           uint32_t xy[], int count, int x, int y) {
     SkASSERT(!s.fInvMatrix.hasPerspective());
@@ -212,8 +215,8 @@
     unsigned maxX = s.fPixmap.width () - 1,
              maxY = s.fPixmap.height() - 1;
     while (count --> 0) {
-        *xy++ = pack<tile, extract_low_bits>(SkFractionalIntToFixed(fy), maxY, oneY);
-        *xy++ = pack<tile, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, oneX);
+        *xy++ = pack<tiley, extract_low_bits>(SkFractionalIntToFixed(fy), maxY, oneY);
+        *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, oneX);
 
         fy += dy;
         fx += dx;
@@ -226,9 +229,6 @@
     return x >> 16;
 }
 
-static unsigned clamp(SkFixed fx, int max) {
-    return SkClampMax(fx >> 16, max);
-}
 static unsigned repeat(SkFixed fx, int max) {
     SkASSERT(max < 65535);
     return SK_USHIFT16((unsigned)(fx & 0xFFFF) * (max + 1));
@@ -242,17 +242,29 @@
     return SK_USHIFT16( ((fx ^ s) & 0xFFFF) * (max + 1) );
 }
 
+static unsigned clamp(SkFixed fx, int max) {
+    return SkClampMax(fx >> 16, max);
+}
+
+#if defined(SK_SUPPORT_LEGACY_TILED_BITMAPS)
+ // For use with extract_low_bits_general(), where clamp() above expects extract_low_bits_clamp_clamp()
+static unsigned general_clamp(SkFixed fx, int max)
+{
+    return repeat(SkClampMax(fx, 0xFFFF), max);
+}
+#endif
+
 static const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = {
-    nofilter_scale <clamp, true>, filter_scale <clamp, extract_low_bits_clamp, true>,
-    nofilter_affine<clamp>,       filter_affine<clamp, extract_low_bits_clamp>,
+    nofilter_scale <clamp, clamp, true>, filter_scale <clamp, clamp, extract_low_bits_clamp_clamp, true>,
+    nofilter_affine<clamp, clamp>,       filter_affine<clamp, clamp, extract_low_bits_clamp_clamp>,
 };
 static const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs[] = {
-    nofilter_scale <repeat, false>, filter_scale <repeat, extract_low_bits_repeat_mirror,false>,
-    nofilter_affine<repeat>,        filter_affine<repeat, extract_low_bits_repeat_mirror>,
+    nofilter_scale <repeat, repeat, false>, filter_scale <repeat, repeat, extract_low_bits_general, false>,
+    nofilter_affine<repeat, repeat>,        filter_affine<repeat, repeat, extract_low_bits_general>
 };
 static const SkBitmapProcState::MatrixProc MirrorX_MirrorY_Procs[] = {
-    nofilter_scale <mirror, false>, filter_scale <mirror, extract_low_bits_repeat_mirror, false>,
-    nofilter_affine<mirror>,        filter_affine<mirror, extract_low_bits_repeat_mirror>,
+    nofilter_scale <mirror, mirror,  false>, filter_scale <mirror, mirror, extract_low_bits_general, false>,
+    nofilter_affine<mirror, mirror>,         filter_affine<mirror, mirror, extract_low_bits_general>,
 };
 
 
@@ -309,12 +321,13 @@
     }
 }
 
+template< U16CPU (tiley)(int x, int n) >
 static void clampx_nofilter_trans(const SkBitmapProcState& s,
                                   uint32_t xy[], int count, int x, int y) {
     SkASSERT(s.fInvMatrix.isTranslate());
 
     const SkBitmapProcStateAutoMapper mapper(s, x, y);
-    *xy++ = int_clamp(mapper.intY(), s.fPixmap.height());
+    *xy++ = tiley(mapper.intY(), s.fPixmap.height());
     int xpos = mapper.intX();
 
     const int width = s.fPixmap.width();
@@ -360,12 +373,13 @@
     sk_memset16(xptr, width - 1, count);
 }
 
+template< U16CPU (tiley)(int x, int n) >
 static void repeatx_nofilter_trans(const SkBitmapProcState& s,
                                    uint32_t xy[], int count, int x, int y) {
     SkASSERT(s.fInvMatrix.isTranslate());
 
     const SkBitmapProcStateAutoMapper mapper(s, x, y);
-    *xy++ = int_repeat(mapper.intY(), s.fPixmap.height());
+    *xy++ = tiley(mapper.intY(), s.fPixmap.height());
     int xpos = mapper.intX();
 
     const int width = s.fPixmap.width();
@@ -396,12 +410,13 @@
     }
 }
 
+template< U16CPU (tiley)(int x, int n) >
 static void mirrorx_nofilter_trans(const SkBitmapProcState& s,
                                    uint32_t xy[], int count, int x, int y) {
     SkASSERT(s.fInvMatrix.isTranslate());
 
     const SkBitmapProcStateAutoMapper mapper(s, x, y);
-    *xy++ = int_mirror(mapper.intY(), s.fPixmap.height());
+    *xy++ = tiley(mapper.intY(), s.fPixmap.height());
     int xpos = mapper.intX();
 
     const int width = s.fPixmap.width();
@@ -456,44 +471,127 @@
     }
 }
 
+
+#if defined(SK_SUPPORT_LEGACY_TILED_BITMAPS)
+
+// below table is arranged as given below
+// first special optimized translate only MatrixProcs are put for all tilex and tiley configurations
+// then nofilter_scale MatrixProcs are put for all tilex and tiley configurations
+// then filter_scale MatrixProcs are put for all tilex and tiley configurations
+// then nofilter_affine MatrixProcs are put for all tilex and tiley configurations
+// then filter_affine MatrixProcs are put for all tilex and tiley configurations
+static const SkBitmapProcState::MatrixProc GeneralProcs[] = {
+        // below entries are configured using translateOnlyMatrixProcType<tiley>
+        clampx_nofilter_trans<int_repeat>,
+        clampx_nofilter_trans<int_mirror>,
+        repeatx_nofilter_trans<int_clamp>,
+        repeatx_nofilter_trans<int_mirror>,
+        mirrorx_nofilter_trans<int_clamp>,
+        mirrorx_nofilter_trans<int_repeat>,
+        // below proc entries are configured using nofilter_scale< tilex, tiley, tryDecal >
+        nofilter_scale< general_clamp, repeat, false>,
+        nofilter_scale< general_clamp, mirror, false>,
+        nofilter_scale< repeat,        general_clamp, false>,
+        nofilter_scale< repeat,        mirror, false>,
+        nofilter_scale< mirror,        general_clamp, false>,
+        nofilter_scale< mirror,        repeat, false>,
+        // below proc entries are configured using filter_scale< tilex, tiley, extract_low_bits, tryDecal >
+        filter_scale<   general_clamp, repeat,        extract_low_bits_general, false>,
+        filter_scale<   general_clamp, mirror,        extract_low_bits_general, false>,
+        filter_scale<   repeat,        general_clamp, extract_low_bits_general, false>,
+        filter_scale<   repeat,        mirror,        extract_low_bits_general, false>,
+        filter_scale<   mirror,        general_clamp, extract_low_bits_general, false>,
+        filter_scale<   mirror,        repeat,        extract_low_bits_general, false>,
+        // below proc entries are configured using nofilter_affine< tilex, tiley >
+        nofilter_affine<general_clamp, repeat>,
+        nofilter_affine<general_clamp, mirror>,
+        nofilter_affine<repeat,        general_clamp>,
+        nofilter_affine<repeat,        mirror>,
+        nofilter_affine<mirror,        general_clamp>,
+        nofilter_affine<mirror,        repeat>,
+        // below proc entries are configured using filter_affine< tilex, tiley, extract_low_bits >
+        filter_affine<  general_clamp, repeat,        extract_low_bits_general>,
+        filter_affine<  general_clamp, mirror,        extract_low_bits_general>,
+        filter_affine<  repeat,        general_clamp, extract_low_bits_general>,
+        filter_affine<  repeat,        mirror,        extract_low_bits_general>,
+        filter_affine<  mirror,        general_clamp, extract_low_bits_general>,
+        filter_affine<  mirror,        repeat,        extract_low_bits_general>};
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // The main entry point to the file, choosing between everything above.
 
 SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool translate_only_matrix) {
     SkASSERT(!fInvMatrix.hasPerspective());
-    SkASSERT(fTileModeX == fTileModeY);
     SkASSERT(fTileModeX != SkTileMode::kDecal);
 
-    // Check for our special case translate methods when there is no scale/affine/perspective.
-    if (translate_only_matrix && kNone_SkFilterQuality == fFilterQuality) {
-        switch (fTileModeX) {
-            default: SkASSERT(false);
-            case SkTileMode::kClamp:  return  clampx_nofilter_trans;
-            case SkTileMode::kRepeat: return repeatx_nofilter_trans;
-            case SkTileMode::kMirror: return mirrorx_nofilter_trans;
+    if( fTileModeX == fTileModeY ) {
+        // Check for our special case translate methods when there is no scale/affine/perspective.
+        if (translate_only_matrix && kNone_SkFilterQuality == fFilterQuality) {
+            switch (fTileModeX) {
+                default: SkASSERT(false);
+                case SkTileMode::kClamp:  return  clampx_nofilter_trans<int_clamp>;
+                case SkTileMode::kRepeat: return repeatx_nofilter_trans<int_repeat>;
+                case SkTileMode::kMirror: return mirrorx_nofilter_trans<int_mirror>;
+            }
         }
+
+        // The arrays are all [ nofilter, filter ].
+        int index = fFilterQuality > kNone_SkFilterQuality ? 1 : 0;
+        if (!fInvMatrix.isScaleTranslate()) {
+            index |= 2;
+        }
+
+        if (fTileModeX == SkTileMode::kClamp) {
+            // clamp gets special version of filterOne, working in non-normalized space (allowing decal)
+            fFilterOneX = SK_Fixed1;
+            fFilterOneY = SK_Fixed1;
+            return ClampX_ClampY_Procs[index];
+        }
+
+        // all remaining procs use this form for filterOne, putting them into normalized space.
+        fFilterOneX = SK_Fixed1 / fPixmap.width();
+        fFilterOneY = SK_Fixed1 / fPixmap.height();
+
+        if (fTileModeX == SkTileMode::kRepeat) {
+            return RepeatX_RepeatY_Procs[index];
+        }
+
+        return MirrorX_MirrorY_Procs[index];
     }
 
-    // The arrays are all [ nofilter, filter ].
-    int index = fFilterQuality > kNone_SkFilterQuality ? 1 : 0;
+
+#if defined(SK_SUPPORT_LEGACY_TILED_BITMAPS)
+
+    int index = translate_only_matrix && kNone_SkFilterQuality == fFilterQuality ? 0 : 6;
+
+    index += ( fFilterQuality > kNone_SkFilterQuality ? 6 : 0 );
+
     if (!fInvMatrix.isScaleTranslate()) {
-        index |= 2;
+        index += 2 * 6;
     }
 
-    if (fTileModeX == SkTileMode::kClamp) {
-        // clamp gets special version of filterOne, working in non-normalized space (allowing decal)
-        fFilterOneX = SK_Fixed1;
-        fFilterOneY = SK_Fixed1;
-        return ClampX_ClampY_Procs[index];
+    if (fTileModeX == SkTileMode::kRepeat) {
+        index = index + 2;
+    }
+    else if (fTileModeX == SkTileMode::kMirror) {
+        index = index + 4;
     }
 
-    // all remaining procs use this form for filterOne, putting them into normalized space.
+    if (fTileModeY == SkTileMode::kMirror ||
+        (fTileModeY == SkTileMode::kRepeat && fTileModeX == SkTileMode::kMirror)) {
+        index += 1;
+    }
+
     fFilterOneX = SK_Fixed1 / fPixmap.width();
     fFilterOneY = SK_Fixed1 / fPixmap.height();
 
-    if (fTileModeX == SkTileMode::kRepeat) {
-        return RepeatX_RepeatY_Procs[index];
-    }
+    return GeneralProcs[ index ];
 
-    return MirrorX_MirrorY_Procs[index];
+#else
+
+    SkASSERT(fTileModeX == fTileModeY);
+    return nullptr;
+
+#endif
 }
diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h
index c5e688a..b19e565 100644
--- a/src/core/SkBlendModePriv.h
+++ b/src/core/SkBlendModePriv.h
@@ -23,23 +23,6 @@
 bool SkBlendMode_ShouldPreScaleCoverage(SkBlendMode, bool rgb_coverage);
 void SkBlendMode_AppendStages(SkBlendMode, SkRasterPipeline*);
 
-enum class SkBlendModeCoeff {
-    kZero, /** 0 */
-    kOne,  /** 1 */
-    kSC,   /** src color */
-    kISC,  /** inverse src color (i.e. 1 - sc) */
-    kDC,   /** dst color */
-    kIDC,  /** inverse dst color (i.e. 1 - dc) */
-    kSA,   /** src alpha */
-    kISA,  /** inverse src alpha (i.e. 1 - sa) */
-    kDA,   /** dst alpha */
-    kIDA,  /** inverse dst alpha (i.e. 1 - da) */
-
-    kCoeffCount
-};
-
-bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst);
-
 SkPMColor4f SkBlendMode_Apply(SkBlendMode, const SkPMColor4f& src, const SkPMColor4f& dst);
 
 #if SK_SUPPORT_GPU
diff --git a/src/core/SkBlitter_RGB565.cpp b/src/core/SkBlitter_RGB565.cpp
index 3df8217..80087a7 100644
--- a/src/core/SkBlitter_RGB565.cpp
+++ b/src/core/SkBlitter_RGB565.cpp
@@ -10,7 +10,6 @@
 #include "include/private/SkColorData.h"
 #include "src/core/SkCoreBlitters.h"
 #include "src/core/SkXfermodePriv.h"
-#include "src/utils/SkUTF.h"
 
 #include "include/private/SkNx.h"
 
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
index 89b107b..701607e 100644
--- a/src/core/SkBlitter_Sprite.cpp
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -56,8 +56,7 @@
 class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter {
 public:
     static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) {
-        // the caller has already inspected the colorspace on src and dst
-        SkASSERT(!SkColorSpaceXformSteps::Required(src.colorSpace(), dst.colorSpace()));
+        SkASSERT(0 == SkColorSpaceXformSteps(src, dst).flags.mask());
 
         if (dst.colorType() != src.colorType()) {
             return false;
@@ -178,13 +177,14 @@
     */
     SkASSERT(allocator != nullptr);
 
+    // TODO: in principle SkRasterPipelineSpriteBlitter could be made to handle this.
     if (source.alphaType() == kUnpremul_SkAlphaType) {
         return nullptr;
     }
 
     SkSpriteBlitter* blitter = nullptr;
 
-    if (!SkColorSpaceXformSteps::Required(source.colorSpace(), dst.colorSpace())) {
+    if (0 == SkColorSpaceXformSteps(source,dst).flags.mask()) {
         if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) {
             blitter = allocator->make<SkSpriteBlitter_Memcpy>(source);
         }
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index d385af8..cb19ea4 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -370,7 +370,7 @@
         SkPath path;
         path.addRect(rects[0]);
         path.addRect(rects[1]);
-        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.setFillType(SkPathFillType::kEvenOdd);
         canvas.drawPath(path, paint);
     }
     return true;
@@ -901,9 +901,8 @@
     if (!isNormalBlur) {
         GrPaint paint;
         // Blend pathTexture over blurTexture.
-        paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(srcProxy),
-                                                                       srcColorType,
-                                                                       SkMatrix::I()));
+        paint.addCoverageFragmentProcessor(
+                GrSimpleTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I()));
         if (kInner_SkBlurStyle == fBlurStyle) {
             // inner:  dst = dst * src
             paint.setCoverageSetOpXPFactory(SkRegion::kIntersect_Op);
diff --git a/src/core/SkBlurMask.h b/src/core/SkBlurMask.h
index 55c999e..b7f790e 100644
--- a/src/core/SkBlurMask.h
+++ b/src/core/SkBlurMask.h
@@ -46,9 +46,9 @@
                                                       SkBlurStyle, SkIPoint* margin = nullptr);
 
     // If radius > 0, return the corresponding sigma, else return 0
-    static SkScalar SK_API ConvertRadiusToSigma(SkScalar radius);
+    static SkScalar SK_SPI ConvertRadiusToSigma(SkScalar radius);
     // If sigma > 0.5, return the corresponding radius, else return 0
-    static SkScalar SK_API ConvertSigmaToRadius(SkScalar sigma);
+    static SkScalar SK_SPI ConvertSigmaToRadius(SkScalar sigma);
 
     /* Helper functions for analytic rectangle blurs */
 
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 90335e87..9dd0f16 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -54,6 +54,9 @@
 #define RETURN_ON_NULL(ptr)     do { if (nullptr == (ptr)) return; } while (0)
 #define RETURN_ON_FALSE(pred)   do { if (!(pred)) return; } while (0)
 
+// This is a test: static_assert with no message is a c++17 feature.
+static_assert(true);
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /*
@@ -616,6 +619,10 @@
     }
 }
 
+SkSurface* SkCanvas::getSurface() const {
+    return fSurfaceBase;
+}
+
 SkISize SkCanvas::getBaseLayerSize() const {
     SkBaseDevice* d = this->getDevice();
     return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 9219f67..f8dad04 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -129,13 +129,13 @@
         case DeviceSpaceType::kRect:
             fDeviceSpacePath.init();
             fDeviceSpacePath.get()->addRect(this->getDeviceSpaceRect());
-            fDeviceSpacePath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
+            fDeviceSpacePath.get()->setFillType(SkPathFillType::kInverseEvenOdd);
             fDeviceSpaceType = DeviceSpaceType::kPath;
             break;
         case DeviceSpaceType::kRRect:
             fDeviceSpacePath.init();
             fDeviceSpacePath.get()->addRRect(fDeviceSpaceRRect);
-            fDeviceSpacePath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
+            fDeviceSpacePath.get()->setFillType(SkPathFillType::kInverseEvenOdd);
             fDeviceSpaceType = DeviceSpaceType::kPath;
             break;
         case DeviceSpaceType::kPath:
@@ -752,7 +752,7 @@
     bool isAA = false;
 
     path->reset();
-    path->setFillType(SkPath::kInverseEvenOdd_FillType);
+    path->setFillType(SkPathFillType::kInverseEvenOdd);
 
     SkClipStack::Iter iter(*this, SkClipStack::Iter::kBottom_IterStart);
     while (const SkClipStack::Element* element = iter.next()) {
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index babab34..8920bde 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -17,6 +17,7 @@
 #include "src/core/SkColorSpaceXformSteps.h"
 #include "src/core/SkRasterPipeline.h"
 #include "src/core/SkReadBuffer.h"
+#include "src/core/SkVM.h"
 #include "src/core/SkWriteBuffer.h"
 
 #if SK_SUPPORT_GPU
@@ -43,6 +44,13 @@
     return this->onAppendStages(rec, shaderIsOpaque);
 }
 
+bool SkColorFilter::program(skvm::Builder*,
+                            SkColorSpace* dstCS,
+                            skvm::Uniforms* uniforms,
+                            skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    return false;
+}
+
 SkColor SkColorFilter::filterColor(SkColor c) const {
     // This is mostly meaningless. We should phase-out this call entirely.
     SkColorSpace* cs = nullptr;
diff --git a/src/core/SkColorFilterPriv.h b/src/core/SkColorFilterPriv.h
index a386e6c..ca6ad35 100644
--- a/src/core/SkColorFilterPriv.h
+++ b/src/core/SkColorFilterPriv.h
@@ -14,7 +14,7 @@
 
 using SkRuntimeColorFilterFn = void(*)(float[4], const void*);
 
-class SK_API SkRuntimeColorFilterFactory {
+class SK_SPI SkRuntimeColorFilterFactory {
 public:
     /**
      * Creates a factory which creates runtime color filters. The SkSL must define a 'main' function
diff --git a/src/core/SkColorFilter_Matrix.cpp b/src/core/SkColorFilter_Matrix.cpp
index dd112a3..53368fb 100644
--- a/src/core/SkColorFilter_Matrix.cpp
+++ b/src/core/SkColorFilter_Matrix.cpp
@@ -79,6 +79,50 @@
     return true;
 }
 
+bool SkColorFilter_Matrix::program(skvm::Builder* p,
+                                   SkColorSpace* /*dstCS*/,
+                                   skvm::Uniforms* uniforms,
+                                   skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    // TODO: specialize generated code on the 0/1 values of fMatrix?
+    if (fDomain == Domain::kRGBA) {
+        // Unpremul.
+        skvm::F32 invA = p->div(p->splat(1.0f), *a),
+                  inf  = p->bit_cast(p->splat(0x7f800000));
+
+        // If *a is 0, so are *r,*g,*b, so set invA to 0 to avoid 0*inf=NaN (instead 0*0 = 0).
+        invA = p->bit_cast(p->bit_and(p->lt(invA, inf),
+                                      p->bit_cast(invA)));
+        *r = p->mul(*r, invA);
+        *g = p->mul(*g, invA);
+        *b = p->mul(*b, invA);
+
+        // Apply matrix.
+        skvm::Builder::Uniform u = uniforms->pushF(fMatrix, 20);
+        auto m = [&](int i) { return p->uniformF(u.ptr, u.offset + 4*i); };
+
+        skvm::F32 rgba[4];
+        for (int j = 0; j < 4; j++) {
+            rgba[j] =        m(4+j*5);
+            rgba[j] = p->mad(m(3+j*5), *a, rgba[j]);
+            rgba[j] = p->mad(m(2+j*5), *b, rgba[j]);
+            rgba[j] = p->mad(m(1+j*5), *g, rgba[j]);
+            rgba[j] = p->mad(m(0+j*5), *r, rgba[j]);
+        }
+        *r = rgba[0];
+        *g = rgba[1];
+        *b = rgba[2];
+        *a = rgba[3];
+
+        // Premul.
+        *r = p->mul(*r, *a);
+        *g = p->mul(*g, *a);
+        *b = p->mul(*b, *a);
+
+        return true;
+    }
+    return false;
+}
+
 #if SK_SUPPORT_GPU
 #include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
 #include "src/gpu/effects/generated/GrHSLToRGBFilterEffect.h"
@@ -123,7 +167,7 @@
 }
 
 sk_sp<SkColorFilter> SkColorFilters::Matrix(const SkColorMatrix& cm) {
-    return MakeMatrix(cm.fMat, SkColorFilter_Matrix::Domain::kRGBA);
+    return MakeMatrix(cm.fMat.data(), SkColorFilter_Matrix::Domain::kRGBA);
 }
 
 sk_sp<SkColorFilter> SkColorFilters::HSLAMatrix(const float array[20]) {
diff --git a/src/core/SkColorFilter_Matrix.h b/src/core/SkColorFilter_Matrix.h
index 7f01988..5fe5dd8 100644
--- a/src/core/SkColorFilter_Matrix.h
+++ b/src/core/SkColorFilter_Matrix.h
@@ -26,14 +26,17 @@
 
     static void RegisterFlattenables();
 
-protected:
+private:
     void flatten(SkWriteBuffer&) const override;
     bool onAsAColorMatrix(float matrix[20]) const override;
 
-private:
     SK_FLATTENABLE_HOOKS(SkColorFilter_Matrix)
 
     bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override;
+    bool program(skvm::Builder*,
+                 SkColorSpace* dstCS,
+                 skvm::Uniforms* uniforms,
+                 skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
 
     float       fMatrix[20];
     uint16_t    fFlags;
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
index 4e77d7c..cb72953 100644
--- a/src/core/SkColorSpace.cpp
+++ b/src/core/SkColorSpace.cpp
@@ -15,13 +15,12 @@
     return skcms_PrimariesToXYZD50(fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY, toXYZ_D50);
 }
 
-SkColorSpace::SkColorSpace(const float transferFn[7],
-                           const skcms_Matrix3x3& toXYZD50) {
-    memcpy(fToXYZD50_3x3, &toXYZD50.vals[0][0], 9*sizeof(float));
-    fToXYZD50Hash = SkOpts::hash_fn(fToXYZD50_3x3, 9*sizeof(float), 0);
-
-    memcpy(fTransferFn, transferFn, 7*sizeof(float));
-    fTransferFnHash = SkOpts::hash_fn(fTransferFn, 7*sizeof(float), 0);
+SkColorSpace::SkColorSpace(const skcms_TransferFunction& transferFn,
+                           const skcms_Matrix3x3& toXYZD50)
+        : fTransferFn(transferFn)
+        , fToXYZD50(toXYZD50) {
+    fTransferFnHash = SkOpts::hash_fn(&fTransferFn, 7*sizeof(float), 0);
+    fToXYZD50Hash = SkOpts::hash_fn(&fToXYZD50, 9*sizeof(float), 0);
 }
 
 static bool xyz_almost_equal(const skcms_Matrix3x3& mA, const skcms_Matrix3x3& mB) {
@@ -42,30 +41,30 @@
         return nullptr;
     }
 
-    const float* tf = &transferFn.g;
+    const skcms_TransferFunction* tf = &transferFn;
 
     if (is_almost_srgb(transferFn)) {
         if (xyz_almost_equal(toXYZ, SkNamedGamut::kSRGB)) {
             return SkColorSpace::MakeSRGB();
         }
-        tf = &SkNamedTransferFn::kSRGB.g;
+        tf = &SkNamedTransferFn::kSRGB;
     } else if (is_almost_2dot2(transferFn)) {
-        tf = &SkNamedTransferFn::k2Dot2.g;
+        tf = &SkNamedTransferFn::k2Dot2;
     } else if (is_almost_linear(transferFn)) {
         if (xyz_almost_equal(toXYZ, SkNamedGamut::kSRGB)) {
             return SkColorSpace::MakeSRGBLinear();
         }
-        tf = &SkNamedTransferFn::kLinear.g;
+        tf = &SkNamedTransferFn::kLinear;
     }
 
-    return sk_sp<SkColorSpace>(new SkColorSpace(tf, toXYZ));
+    return sk_sp<SkColorSpace>(new SkColorSpace(*tf, toXYZ));
 }
 
 class SkColorSpaceSingletonFactory {
 public:
     static SkColorSpace* Make(const skcms_TransferFunction& transferFn,
                               const skcms_Matrix3x3& to_xyz) {
-        return new SkColorSpace(&transferFn.g, to_xyz);
+        return new SkColorSpace(transferFn, to_xyz);
     }
 };
 
@@ -94,22 +93,17 @@
 
         // Invert 3x3 gamut, defaulting to sRGB if we can't.
         {
-            skcms_Matrix3x3 fwd, inv;
-            memcpy(&fwd, fToXYZD50_3x3, 9*sizeof(float));
-            if (!skcms_Matrix3x3_invert(&fwd, &inv)) {
-                SkAssertResult(skcms_Matrix3x3_invert(&skcms_sRGB_profile()->toXYZD50, &inv));
+            if (!skcms_Matrix3x3_invert(&fToXYZD50, &fFromXYZD50)) {
+                SkAssertResult(skcms_Matrix3x3_invert(&skcms_sRGB_profile()->toXYZD50,
+                                                      &fFromXYZD50));
             }
-            memcpy(fFromXYZD50_3x3, &inv, 9*sizeof(float));
         }
 
         // Invert transfer function, defaulting to sRGB if we can't.
         {
-            skcms_TransferFunction fwd, inv;
-            this->transferFn(&fwd.g);
-            if (!skcms_TransferFunction_invert(&fwd, &inv)) {
-                inv = *skcms_sRGB_Inverse_TransferFunction();
+            if (!skcms_TransferFunction_invert(&fTransferFn, &fInvTransferFn)) {
+                fInvTransferFn = *skcms_sRGB_Inverse_TransferFunction();
             }
-            memcpy(fInvTransferFn, &inv, 7*sizeof(float));
         }
 
     });
@@ -119,7 +113,7 @@
     // TODO: Change transferFn/invTransferFn to just operate on skcms_TransferFunction (all callers
     // already pass pointers to an skcms struct). Then remove this function, and update the two
     // remaining callers to do the right thing with transferFn and classify.
-    this->transferFn(&coeffs->g);
+    this->transferFn(coeffs);
     return classify_transfer_fn(*coeffs) == sRGBish_TF;
 }
 
@@ -127,32 +121,23 @@
     memcpy(gabcdef, &fTransferFn, 7*sizeof(float));
 }
 
-void SkColorSpace::invTransferFn(float gabcdef[7]) const {
-    this->computeLazyDstFields();
-    memcpy(gabcdef, &fInvTransferFn, 7*sizeof(float));
+void SkColorSpace::transferFn(skcms_TransferFunction* fn) const {
+    *fn = fTransferFn;
 }
 
-bool SkColorSpace::toXYZD50(SkMatrix44* toXYZD50) const {
-    toXYZD50->set3x3RowMajorf(fToXYZD50_3x3);
-    return true;
+void SkColorSpace::invTransferFn(skcms_TransferFunction* fn) const {
+    this->computeLazyDstFields();
+    *fn = fInvTransferFn;
 }
 
 bool SkColorSpace::toXYZD50(skcms_Matrix3x3* toXYZD50) const {
-    memcpy(toXYZD50, fToXYZD50_3x3, 9*sizeof(float));
+    *toXYZD50 = fToXYZD50;
     return true;
 }
 
-void SkColorSpace::gamutTransformTo(const SkColorSpace* dst, float src_to_dst[9]) const {
+void SkColorSpace::gamutTransformTo(const SkColorSpace* dst, skcms_Matrix3x3* src_to_dst) const {
     dst->computeLazyDstFields();
-
-    skcms_Matrix3x3 toXYZD50,
-                  fromXYZD50;
-
-    memcpy(&  toXYZD50, this->  fToXYZD50_3x3, 9*sizeof(float));
-    memcpy(&fromXYZD50, dst ->fFromXYZD50_3x3, 9*sizeof(float));
-
-    skcms_Matrix3x3 srcToDst = skcms_Matrix3x3_concat(&fromXYZD50, &toXYZD50);
-    memcpy(src_to_dst, &srcToDst, 9*sizeof(float));
+    *src_to_dst = skcms_Matrix3x3_concat(&dst->fFromXYZD50, &fToXYZD50);
 }
 
 bool SkColorSpace::isSRGB() const {
@@ -161,30 +146,26 @@
 
 bool SkColorSpace::gammaCloseToSRGB() const {
     // Nearly-equal transfer functions were snapped at construction time, so just do an exact test
-    return memcmp(fTransferFn, &SkNamedTransferFn::kSRGB.g, 7*sizeof(float)) == 0;
+    return memcmp(&fTransferFn, &SkNamedTransferFn::kSRGB, 7*sizeof(float)) == 0;
 }
 
 bool SkColorSpace::gammaIsLinear() const {
     // Nearly-equal transfer functions were snapped at construction time, so just do an exact test
-    return memcmp(fTransferFn, &SkNamedTransferFn::kLinear.g, 7*sizeof(float)) == 0;
+    return memcmp(&fTransferFn, &SkNamedTransferFn::kLinear, 7*sizeof(float)) == 0;
 }
 
 sk_sp<SkColorSpace> SkColorSpace::makeLinearGamma() const {
     if (this->gammaIsLinear()) {
         return sk_ref_sp(const_cast<SkColorSpace*>(this));
     }
-    skcms_Matrix3x3 gamut;
-    this->toXYZD50(&gamut);
-    return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
+    return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, fToXYZD50);
 }
 
 sk_sp<SkColorSpace> SkColorSpace::makeSRGBGamma() const {
     if (this->gammaCloseToSRGB()) {
         return sk_ref_sp(const_cast<SkColorSpace*>(this));
     }
-    skcms_Matrix3x3 gamut;
-    this->toXYZD50(&gamut);
-    return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+    return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, fToXYZD50);
 }
 
 sk_sp<SkColorSpace> SkColorSpace::makeColorSpin() const {
@@ -194,24 +175,15 @@
         { 0, 1, 0 },
     }};
 
-    skcms_Matrix3x3 toXYZ;
-    this->toXYZD50(&toXYZ);
-
-    skcms_Matrix3x3 spun = skcms_Matrix3x3_concat(&toXYZ, &spin);
+    skcms_Matrix3x3 spun = skcms_Matrix3x3_concat(&fToXYZD50, &spin);
 
     return sk_sp<SkColorSpace>(new SkColorSpace(fTransferFn, spun));
 }
 
 void SkColorSpace::toProfile(skcms_ICCProfile* profile) const {
-    skcms_TransferFunction tf;
-    skcms_Matrix3x3        toXYZD50;
-
-    memcpy(&tf,       fTransferFn,   7*sizeof(float));
-    memcpy(&toXYZD50, fToXYZD50_3x3, 9*sizeof(float));
-
     skcms_Init               (profile);
-    skcms_SetTransferFunction(profile, &tf);
-    skcms_SetXYZD50          (profile, &toXYZD50);
+    skcms_SetTransferFunction(profile, &fTransferFn);
+    skcms_SetXYZD50          (profile, &fToXYZD50);
 }
 
 sk_sp<SkColorSpace> SkColorSpace::Make(const skcms_ICCProfile& profile) {
@@ -289,10 +261,10 @@
         *((ColorSpaceHeader*) memory) = ColorSpaceHeader();
         memory = SkTAddOffset<void>(memory, sizeof(ColorSpaceHeader));
 
-        memcpy(memory, fTransferFn, 7 * sizeof(float));
+        memcpy(memory, &fTransferFn, 7 * sizeof(float));
         memory = SkTAddOffset<void>(memory, 7 * sizeof(float));
 
-        memcpy(memory, fToXYZD50_3x3, 9 * sizeof(float));
+        memcpy(memory, &fToXYZD50, 9 * sizeof(float));
     }
 
     return sizeof(ColorSpaceHeader) + 16 * sizeof(float);
@@ -408,10 +380,12 @@
 
     if (x->hash() == y->hash()) {
         for (int i = 0; i < 7; i++) {
-            SkASSERT(x->  fTransferFn[i] == y->  fTransferFn[i] && "Hash collsion");
+            SkASSERT((&x->fTransferFn.g)[i] == (&y->fTransferFn.g)[i] && "Hash collsion");
         }
-        for (int i = 0; i < 9; i++) {
-            SkASSERT(x->fToXYZD50_3x3[i] == y->fToXYZD50_3x3[i] && "Hash collsion");
+        for (int r = 0; r < 3; r++) {
+            for (int c = 0; c < 3; ++c) {
+                SkASSERT(x->fToXYZD50.vals[r][c] == y->fToXYZD50.vals[r][c] && "Hash collsion");
+            }
         }
         return true;
     }
diff --git a/src/core/SkColorSpacePriv.h b/src/core/SkColorSpacePriv.h
index 425357d..2e8728c 100644
--- a/src/core/SkColorSpacePriv.h
+++ b/src/core/SkColorSpacePriv.h
@@ -41,10 +41,10 @@
 static inline TFKind classify_transfer_fn(const skcms_TransferFunction& tf) {
     if (tf.g < 0 && (int)tf.g == tf.g) {
         // TODO: sanity checks for PQ/HLG like we do for sRGBish.
-        switch (-(int)tf.g) {
-            case PQish_TF:     return PQish_TF;
-            case HLGish_TF:    return HLGish_TF;
-            case HLGinvish_TF: return HLGinvish_TF;
+        switch ((int)tf.g) {
+            case -PQish_TF:     return PQish_TF;
+            case -HLGish_TF:    return HLGish_TF;
+            case -HLGinvish_TF: return HLGinvish_TF;
         }
         return Bad_TF;
     }
diff --git a/src/core/SkColorSpaceXformSteps.cpp b/src/core/SkColorSpaceXformSteps.cpp
index fe03bca..691ecaf 100644
--- a/src/core/SkColorSpaceXformSteps.cpp
+++ b/src/core/SkColorSpaceXformSteps.cpp
@@ -10,16 +10,6 @@
 #include "src/core/SkColorSpaceXformSteps.h"
 #include "src/core/SkRasterPipeline.h"
 
-// TODO(mtklein): explain the logic of this file
-
-bool SkColorSpaceXformSteps::Required(SkColorSpace* src, SkColorSpace* dst) {
-    // Any SkAlphaType will work fine here as long as we use the same one.
-    SkAlphaType at = kPremul_SkAlphaType;
-    return 0 != SkColorSpaceXformSteps(src, at,
-                                       dst, at).flags.mask();
-    // TODO(mtklein): quicker impl. that doesn't construct an SkColorSpaceXformSteps?
-}
-
 SkColorSpaceXformSteps::SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
                                                SkColorSpace* dst, SkAlphaType dstAT) {
     // Opaque outputs are treated as the same alpha type as the source input.
@@ -45,20 +35,20 @@
     this->flags.premul          = srcAT != kOpaque_SkAlphaType && dstAT == kPremul_SkAlphaType;
 
     if (this->flags.gamut_transform) {
-        float row_major[9];  // TODO: switch src_to_dst_matrix to row-major
-        src->gamutTransformTo(dst, row_major);
+        skcms_Matrix3x3 src_to_dst;  // TODO: switch src_to_dst_matrix to row-major
+        src->gamutTransformTo(dst, &src_to_dst);
 
-        this->src_to_dst_matrix[0] = row_major[0];
-        this->src_to_dst_matrix[1] = row_major[3];
-        this->src_to_dst_matrix[2] = row_major[6];
+        this->src_to_dst_matrix[0] = src_to_dst.vals[0][0];
+        this->src_to_dst_matrix[1] = src_to_dst.vals[1][0];
+        this->src_to_dst_matrix[2] = src_to_dst.vals[2][0];
 
-        this->src_to_dst_matrix[3] = row_major[1];
-        this->src_to_dst_matrix[4] = row_major[4];
-        this->src_to_dst_matrix[5] = row_major[7];
+        this->src_to_dst_matrix[3] = src_to_dst.vals[0][1];
+        this->src_to_dst_matrix[4] = src_to_dst.vals[1][1];
+        this->src_to_dst_matrix[5] = src_to_dst.vals[2][1];
 
-        this->src_to_dst_matrix[6] = row_major[2];
-        this->src_to_dst_matrix[7] = row_major[5];
-        this->src_to_dst_matrix[8] = row_major[8];
+        this->src_to_dst_matrix[6] = src_to_dst.vals[0][2];
+        this->src_to_dst_matrix[7] = src_to_dst.vals[1][2];
+        this->src_to_dst_matrix[8] = src_to_dst.vals[2][2];
     } else {
     #ifdef SK_DEBUG
         skcms_Matrix3x3 srcM, dstM;
@@ -69,8 +59,8 @@
     }
 
     // Fill out all the transfer functions we'll use.
-    src->   transferFn(&this->srcTF   .g);
-    dst->invTransferFn(&this->dstTFInv.g);
+    src->   transferFn(&this->srcTF   );
+    dst->invTransferFn(&this->dstTFInv);
 
     this->srcTF_is_sRGB = src->gammaCloseToSRGB();
     this->dstTF_is_sRGB = dst->gammaCloseToSRGB();
@@ -82,10 +72,10 @@
          src->transferFnHash() == dst->transferFnHash())
     {
     #ifdef SK_DEBUG
-        float dstTF[7];
-        dst->transferFn(dstTF);
+        skcms_TransferFunction dstTF;
+        dst->transferFn(&dstTF);
         for (int i = 0; i < 7; i++) {
-            SkASSERT( (&srcTF.g)[i] == dstTF[i] && "Hash collision" );
+            SkASSERT( (&srcTF.g)[i] == (&dstTF.g)[i] && "Hash collision" );
         }
     #endif
         this->flags.linearize  = false;
diff --git a/src/core/SkColorSpaceXformSteps.h b/src/core/SkColorSpaceXformSteps.h
index 1a15643..e470885 100644
--- a/src/core/SkColorSpaceXformSteps.h
+++ b/src/core/SkColorSpaceXformSteps.h
@@ -10,13 +10,11 @@
 
 #include "include/core/SkColorSpace.h"
 #include "include/core/SkImageInfo.h"
+#include "include/private/SkImageInfoPriv.h"
 
 class SkRasterPipeline;
 
 struct SkColorSpaceXformSteps {
-    // Returns true if SkColorSpaceXformSteps must be applied
-    // to draw content in `src` into a destination in `dst`.
-    static bool Required(SkColorSpace* src, SkColorSpace* dst);
 
     struct Flags {
         bool unpremul         = false;
@@ -37,17 +35,16 @@
     SkColorSpaceXformSteps(SkColorSpace* src, SkAlphaType srcAT,
                            SkColorSpace* dst, SkAlphaType dstAT);
 
+    template <typename S, typename D>
+    SkColorSpaceXformSteps(const S& src, const D& dst)
+        : SkColorSpaceXformSteps(src.colorSpace(), src.alphaType(),
+                                 dst.colorSpace(), dst.alphaType()) {}
+
     void apply(float rgba[4]) const;
     void apply(SkRasterPipeline*, bool src_is_normalized) const;
 
     void apply(SkRasterPipeline* p, SkColorType srcCT) const {
-    #if 0
-        this->apply(p, srcCT < kRGBA_F16_SkColorType);
-    #else
-        // F16Norm is normalized, but to make diffing with F16 easier we
-        // intentionally take the slower, non-normalized path here.
-        this->apply(p, srcCT < kRGBA_F16Norm_SkColorType);
-    #endif
+        return this->apply(p, SkColorTypeIsNormalized(srcCT));
     }
 
     Flags flags;
diff --git a/src/core/SkDescriptor.cpp b/src/core/SkDescriptor.cpp
index 6d6ce37..90406e7 100644
--- a/src/core/SkDescriptor.cpp
+++ b/src/core/SkDescriptor.cpp
@@ -95,19 +95,23 @@
     size_t offset = sizeof(SkDescriptor);
 
     while (lengthRemaining > 0 && count > 0) {
-        const Entry* entry = (const Entry*)(reinterpret_cast<const char*>(this) + offset);
-        // rec tags are always a known size.
-        if (entry->fTag == kRec_SkDescriptorTag && entry->fLen != sizeof(SkScalerContextRec)) {
-            return false;
-        }
         if (lengthRemaining < sizeof(Entry)) {
             return false;
         }
         lengthRemaining -= sizeof(Entry);
+
+        const Entry* entry = (const Entry*)(reinterpret_cast<const char*>(this) + offset);
+
         if (lengthRemaining < entry->fLen) {
             return false;
         }
         lengthRemaining -= entry->fLen;
+
+        // rec tags are always a known size.
+        if (entry->fTag == kRec_SkDescriptorTag && entry->fLen != sizeof(SkScalerContextRec)) {
+            return false;
+        }
+
         offset += sizeof(Entry) + entry->fLen;
         count--;
     }
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index b1d64b7..6a31aee 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -117,7 +117,7 @@
     SkPath path;
     path.addRRect(outer);
     path.addRRect(inner);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.setIsVolatile(true);
 
     this->drawPath(path, paint, true);
diff --git a/src/core/SkDiscardableMemory.h b/src/core/SkDiscardableMemory.h
index 613dbd7..ac5c953 100644
--- a/src/core/SkDiscardableMemory.h
+++ b/src/core/SkDiscardableMemory.h
@@ -15,7 +15,7 @@
  *  Interface for discardable memory. Implementation is provided by the
  *  embedder.
  */
-class SK_API SkDiscardableMemory {
+class SK_SPI SkDiscardableMemory {
 public:
     /**
      *  Factory method that creates, initializes and locks an SkDiscardableMemory
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index e094da6..296329a 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -598,7 +598,7 @@
     draw.fMatrix = matrix;
     SkPath  tmp;
     tmp.addRect(prePaintRect);
-    tmp.setFillType(SkPath::kWinding_FillType);
+    tmp.setFillType(SkPathFillType::kWinding);
     draw.drawPath(tmp, paint, nullptr, true);
 }
 
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index 721217d..365e7c3 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -294,56 +294,32 @@
     SkPathEdgeIter iter(path);
     if (iclip) {
         SkRect clip = this->recoverClip(*iclip);
-        SkEdgeClipper clipper(canCullToTheRight);
+        struct Rec {
+            SkEdgeBuilder* fBuilder;
+            bool           fIsFinite;
+        } rec = { this, true };
 
-        auto apply_clipper = [this, &clipper, &is_finite] {
+        SkEdgeClipper::ClipPath(path, clip, canCullToTheRight,
+                                [](SkEdgeClipper* clipper, bool, void* ctx) {
+            Rec* rec = (Rec*)ctx;
             SkPoint      pts[4];
             SkPath::Verb verb;
 
-            while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
+            while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) {
                 const int count = SkPathPriv::PtsInIter(verb);
                 if (!SkScalarsAreFinite(&pts[0].fX, count*2)) {
-                    is_finite = false;
+                    rec->fIsFinite = false;
                     return;
                 }
                 switch (verb) {
-                    case SkPath::kLine_Verb:  this->addLine (pts); break;
-                    case SkPath::kQuad_Verb:  this->addQuad (pts); break;
-                    case SkPath::kCubic_Verb: this->addCubic(pts); break;
+                    case SkPath::kLine_Verb:  rec->fBuilder->addLine (pts); break;
+                    case SkPath::kQuad_Verb:  rec->fBuilder->addQuad (pts); break;
+                    case SkPath::kCubic_Verb: rec->fBuilder->addCubic(pts); break;
                     default: break;
                 }
             }
-        };
-
-        while (auto e = iter.next()) {
-            switch (e.fEdge) {
-                case SkPathEdgeIter::Edge::kLine:
-                    if (clipper.clipLine(e.fPts[0], e.fPts[1], clip)) {
-                        apply_clipper();
-                    }
-                    break;
-                case SkPathEdgeIter::Edge::kQuad:
-                    if (clipper.clipQuad(e.fPts, clip)) {
-                        apply_clipper();
-                    }
-                    break;
-                case SkPathEdgeIter::Edge::kConic: {
-                    const SkPoint* quadPts = quadder.computeQuads(
-                                          e.fPts, iter.conicWeight(), conicTol);
-                    for (int i = 0; i < quadder.countQuads(); ++i) {
-                        if (clipper.clipQuad(quadPts, clip)) {
-                            apply_clipper();
-                        }
-                        quadPts += 2;
-                    }
-                } break;
-                case SkPathEdgeIter::Edge::kCubic:
-                    if (clipper.clipCubic(e.fPts, clip)) {
-                        apply_clipper();
-                    }
-                    break;
-            }
-        }
+        }, &rec);
+        is_finite = rec.fIsFinite;
     } else {
         auto handle_quad = [this](const SkPoint pts[3]) {
             SkPoint monoX[5];
diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp
index fcadd3e..ef660d1 100644
--- a/src/core/SkEdgeClipper.cpp
+++ b/src/core/SkEdgeClipper.cpp
@@ -555,3 +555,43 @@
     }
 }
 #endif
+
+#include "src/core/SkPathPriv.h"
+
+void SkEdgeClipper::ClipPath(const SkPath& path, const SkRect& clip, bool canCullToTheRight,
+                             void (*consume)(SkEdgeClipper*, bool newCtr, void* ctx), void* ctx) {
+    SkAutoConicToQuads quadder;
+    const SkScalar conicTol = SK_Scalar1 / 4;
+
+    SkPathEdgeIter iter(path);
+    SkEdgeClipper clipper(canCullToTheRight);
+
+    while (auto e = iter.next()) {
+        switch (e.fEdge) {
+            case SkPathEdgeIter::Edge::kLine:
+                if (clipper.clipLine(e.fPts[0], e.fPts[1], clip)) {
+                    consume(&clipper, e.fIsNewContour, ctx);
+                }
+                break;
+            case SkPathEdgeIter::Edge::kQuad:
+                if (clipper.clipQuad(e.fPts, clip)) {
+                    consume(&clipper, e.fIsNewContour, ctx);
+                }
+                break;
+            case SkPathEdgeIter::Edge::kConic: {
+                const SkPoint* quadPts = quadder.computeQuads(e.fPts, iter.conicWeight(), conicTol);
+                for (int i = 0; i < quadder.countQuads(); ++i) {
+                    if (clipper.clipQuad(quadPts, clip)) {
+                        consume(&clipper, e.fIsNewContour, ctx);
+                    }
+                    quadPts += 2;
+                }
+            } break;
+            case SkPathEdgeIter::Edge::kCubic:
+                if (clipper.clipCubic(e.fPts, clip)) {
+                    consume(&clipper, e.fIsNewContour, ctx);
+                }
+                break;
+        }
+    }
+}
diff --git a/src/core/SkEdgeClipper.h b/src/core/SkEdgeClipper.h
index 2718ed7..4d3a87c 100644
--- a/src/core/SkEdgeClipper.h
+++ b/src/core/SkEdgeClipper.h
@@ -26,6 +26,13 @@
 
     bool canCullToTheRight() const { return fCanCullToTheRight; }
 
+    /**
+     *  Clips each segment from the path, and passes the result (in a clipper) to the
+     *  consume proc.
+     */
+    static void ClipPath(const SkPath& path, const SkRect& clip, bool canCullToTheRight,
+                         void (*consume)(SkEdgeClipper*, bool newCtr, void* ctx), void* ctx);
+
 private:
     SkPoint*        fCurrPoint;
     SkPath::Verb*   fCurrVerb;
diff --git a/src/core/SkEnumerate.h b/src/core/SkEnumerate.h
index 801aa54..52e19d6 100644
--- a/src/core/SkEnumerate.h
+++ b/src/core/SkEnumerate.h
@@ -14,11 +14,20 @@
 
 #include "include/private/SkTLogic.h"
 
-// SkEnumerate returns a tuple with an index and the value returned by the iterator. The index always
-// starts at 0.
 template <typename Iter, typename C = skstd::monostate>
 class SkEnumerate {
-    using Result = std::tuple<size_t, decltype(*std::declval<Iter>())>;
+    using Captured = decltype(*std::declval<Iter>());
+    template <typename> struct is_tuple : std::false_type {};
+    template <typename... T> struct is_tuple<std::tuple<T...>> : std::true_type {};
+    static constexpr auto MakeResult(size_t i, Captured&& v) {
+        if constexpr (is_tuple<Captured>::value) {
+            return std::tuple_cat(std::tuple<size_t>{i}, std::forward<Captured>(v));
+        } else {
+            return std::tuple_cat(std::tuple<size_t>{i},
+                    std::make_tuple(std::forward<Captured>(v)));
+        }
+    }
+    using Result = decltype(MakeResult(0, std::declval<Captured>()));
 
     class Iterator {
     public:
@@ -33,7 +42,7 @@
         constexpr Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
         constexpr bool operator==(const Iterator& rhs) const { return fIt == rhs.fIt; }
         constexpr bool operator!=(const Iterator& rhs) const { return fIt != rhs.fIt; }
-        constexpr reference operator*() { return std::forward_as_tuple(fIndex, *fIt); }
+        constexpr reference operator*() { return MakeResult(fIndex, *fIt); }
 
     private:
         ptrdiff_t fIndex;
@@ -49,7 +58,8 @@
     constexpr SkEnumerate(const SkEnumerate& that) = default;
     constexpr SkEnumerate& operator=(const SkEnumerate& that) {
         fBegin = that.fBegin;
-        fEnd = that.fEnd; return *this;
+        fEnd = that.fEnd;
+        return *this;
     }
     constexpr Iterator begin() const { return Iterator{0, fBegin}; }
     constexpr Iterator end() const { return Iterator{fEnd - fBegin, fEnd}; }
diff --git a/src/core/SkFont.cpp b/src/core/SkFont.cpp
index fdfbf66..e2e3ef9 100644
--- a/src/core/SkFont.cpp
+++ b/src/core/SkFont.cpp
@@ -432,112 +432,3 @@
         text[i] = (id < numGlyphsInTypeface) ? unichars[id] : 0xFFFD;
     }
 }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-#include "src/core/SkReadBuffer.h"
-#include "src/core/SkWriteBuffer.h"
-
-// packed int at the beginning of the serialized font:
-//
-//  control_bits:8 size_as_byte:8 flags:12 edging:2 hinting:2
-
-enum {
-    kSize_Is_Byte_Bit   = 1 << 31,
-    kHas_ScaleX_Bit     = 1 << 30,
-    kHas_SkewX_Bit      = 1 << 29,
-    kHas_Typeface_Bit   = 1 << 28,
-
-    kShift_for_Size     = 16,
-    kMask_For_Size      = 0xFF,
-
-    kShift_For_Flags    = 4,
-    kMask_For_Flags     = 0xFFF,
-
-    kShift_For_Edging   = 2,
-    kMask_For_Edging    = 0x3,
-
-    kShift_For_Hinting  = 0,
-    kMask_For_Hinting   = 0x3
-};
-
-static bool scalar_is_byte(SkScalar x) {
-    int ix = (int)x;
-    return ix == x && ix >= 0 && ix <= kMask_For_Size;
-}
-
-void SkFontPriv::Flatten(const SkFont& font, SkWriteBuffer& buffer) {
-    SkASSERT(font.fFlags <= SkFont::kAllFlags);
-    SkASSERT((font.fFlags & ~kMask_For_Flags) == 0);
-    SkASSERT((font.fEdging & ~kMask_For_Edging) == 0);
-    SkASSERT((font.fHinting & ~kMask_For_Hinting) == 0);
-
-    uint32_t packed = 0;
-    packed |= font.fFlags << kShift_For_Flags;
-    packed |= font.fEdging << kShift_For_Edging;
-    packed |= font.fHinting << kShift_For_Hinting;
-
-    if (scalar_is_byte(font.fSize)) {
-        packed |= kSize_Is_Byte_Bit;
-        packed |= (int)font.fSize << kShift_for_Size;
-    }
-    if (font.fScaleX != 1) {
-        packed |= kHas_ScaleX_Bit;
-    }
-    if (font.fSkewX != 0) {
-        packed |= kHas_SkewX_Bit;
-    }
-    if (font.fTypeface) {
-        packed |= kHas_Typeface_Bit;
-    }
-
-    buffer.write32(packed);
-    if (!(packed & kSize_Is_Byte_Bit)) {
-        buffer.writeScalar(font.fSize);
-    }
-    if (packed & kHas_ScaleX_Bit) {
-        buffer.writeScalar(font.fScaleX);
-    }
-    if (packed & kHas_SkewX_Bit) {
-        buffer.writeScalar(font.fSkewX);
-    }
-    if (packed & kHas_Typeface_Bit) {
-        buffer.writeTypeface(font.fTypeface.get());
-    }
-}
-
-bool SkFontPriv::Unflatten(SkFont* font, SkReadBuffer& buffer) {
-    const uint32_t packed = buffer.read32();
-
-    if (packed & kSize_Is_Byte_Bit) {
-        font->fSize = (packed >> kShift_for_Size) & kMask_For_Size;
-    } else {
-        font->fSize = buffer.readScalar();
-    }
-    if (packed & kHas_ScaleX_Bit) {
-        font->fScaleX = buffer.readScalar();
-    }
-    if (packed & kHas_SkewX_Bit) {
-        font->fSkewX = buffer.readScalar();
-    }
-    if (packed & kHas_Typeface_Bit) {
-        font->fTypeface = buffer.readTypeface();
-    }
-
-    SkASSERT(SkFont::kAllFlags <= kMask_For_Flags);
-    // we & with kAllFlags, to clear out any unknown flag bits
-    font->fFlags = SkToU8((packed >> kShift_For_Flags) & SkFont::kAllFlags);
-
-    unsigned edging = (packed >> kShift_For_Edging) & kMask_For_Edging;
-    if (edging > (unsigned)SkFont::Edging::kSubpixelAntiAlias) {
-        edging = 0;
-    }
-    font->fEdging = SkToU8(edging);
-
-    unsigned hinting = (packed >> kShift_For_Hinting) & kMask_For_Hinting;
-    if (hinting > (unsigned)SkFontHinting::kFull) {
-        hinting = 0;
-    }
-    font->fHinting = SkToU8(hinting);
-
-    return buffer.isValid();
-}
diff --git a/src/core/SkFont_serial.cpp b/src/core/SkFont_serial.cpp
new file mode 100644
index 0000000..040b81a
--- /dev/null
+++ b/src/core/SkFont_serial.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkTypeface.h"
+#include "include/private/SkTo.h"
+#include "src/core/SkFontPriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+
+// packed int at the beginning of the serialized font:
+//
+//  control_bits:8 size_as_byte:8 flags:12 edging:2 hinting:2
+
+enum {
+    kSize_Is_Byte_Bit   = 1 << 31,
+    kHas_ScaleX_Bit     = 1 << 30,
+    kHas_SkewX_Bit      = 1 << 29,
+    kHas_Typeface_Bit   = 1 << 28,
+
+    kShift_for_Size     = 16,
+    kMask_For_Size      = 0xFF,
+
+    kShift_For_Flags    = 4,
+    kMask_For_Flags     = 0xFFF,
+
+    kShift_For_Edging   = 2,
+    kMask_For_Edging    = 0x3,
+
+    kShift_For_Hinting  = 0,
+    kMask_For_Hinting   = 0x3
+};
+
+static bool scalar_is_byte(SkScalar x) {
+    int ix = (int)x;
+    return ix == x && ix >= 0 && ix <= kMask_For_Size;
+}
+
+void SkFontPriv::Flatten(const SkFont& font, SkWriteBuffer& buffer) {
+    SkASSERT(font.fFlags <= SkFont::kAllFlags);
+    SkASSERT((font.fFlags & ~kMask_For_Flags) == 0);
+    SkASSERT((font.fEdging & ~kMask_For_Edging) == 0);
+    SkASSERT((font.fHinting & ~kMask_For_Hinting) == 0);
+
+    uint32_t packed = 0;
+    packed |= font.fFlags << kShift_For_Flags;
+    packed |= font.fEdging << kShift_For_Edging;
+    packed |= font.fHinting << kShift_For_Hinting;
+
+    if (scalar_is_byte(font.fSize)) {
+        packed |= kSize_Is_Byte_Bit;
+        packed |= (int)font.fSize << kShift_for_Size;
+    }
+    if (font.fScaleX != 1) {
+        packed |= kHas_ScaleX_Bit;
+    }
+    if (font.fSkewX != 0) {
+        packed |= kHas_SkewX_Bit;
+    }
+    if (font.fTypeface) {
+        packed |= kHas_Typeface_Bit;
+    }
+
+    buffer.write32(packed);
+    if (!(packed & kSize_Is_Byte_Bit)) {
+        buffer.writeScalar(font.fSize);
+    }
+    if (packed & kHas_ScaleX_Bit) {
+        buffer.writeScalar(font.fScaleX);
+    }
+    if (packed & kHas_SkewX_Bit) {
+        buffer.writeScalar(font.fSkewX);
+    }
+    if (packed & kHas_Typeface_Bit) {
+        buffer.writeTypeface(font.fTypeface.get());
+    }
+}
+
+bool SkFontPriv::Unflatten(SkFont* font, SkReadBuffer& buffer) {
+    const uint32_t packed = buffer.read32();
+
+    if (packed & kSize_Is_Byte_Bit) {
+        font->fSize = (packed >> kShift_for_Size) & kMask_For_Size;
+    } else {
+        font->fSize = buffer.readScalar();
+    }
+    if (packed & kHas_ScaleX_Bit) {
+        font->fScaleX = buffer.readScalar();
+    }
+    if (packed & kHas_SkewX_Bit) {
+        font->fSkewX = buffer.readScalar();
+    }
+    if (packed & kHas_Typeface_Bit) {
+        font->fTypeface = buffer.readTypeface();
+    }
+
+    SkASSERT(SkFont::kAllFlags <= kMask_For_Flags);
+    // we & with kAllFlags, to clear out any unknown flag bits
+    font->fFlags = SkToU8((packed >> kShift_For_Flags) & SkFont::kAllFlags);
+
+    unsigned edging = (packed >> kShift_For_Edging) & kMask_For_Edging;
+    if (edging > (unsigned)SkFont::Edging::kSubpixelAntiAlias) {
+        edging = 0;
+    }
+    font->fEdging = SkToU8(edging);
+
+    unsigned hinting = (packed >> kShift_For_Hinting) & kMask_For_Hinting;
+    if (hinting > (unsigned)SkFontHinting::kFull) {
+        hinting = 0;
+    }
+    font->fHinting = SkToU8(hinting);
+
+    return buffer.isValid();
+}
diff --git a/src/core/SkGeometry.h b/src/core/SkGeometry.h
index 23bdbc1..afb9692 100644
--- a/src/core/SkGeometry.h
+++ b/src/core/SkGeometry.h
@@ -89,7 +89,7 @@
     convert it into the cubic fitting the same curve. The new cubic
     curve is returned in dst[0..3].
 */
-SK_API void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]);
+void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]);
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -273,13 +273,13 @@
      *  return the power-of-2 number of quads needed to approximate this conic
      *  with a sequence of quads. Will be >= 0.
      */
-    int SK_API computeQuadPOW2(SkScalar tol) const;
+    int SK_SPI computeQuadPOW2(SkScalar tol) const;
 
     /**
      *  Chop this conic into N quads, stored continguously in pts[], where
      *  N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
      */
-    int SK_API SK_WARN_UNUSED_RESULT chopIntoQuadsPOW2(SkPoint pts[], int pow2) const;
+    int SK_SPI SK_WARN_UNUSED_RESULT chopIntoQuadsPOW2(SkPoint pts[], int pow2) const;
 
     bool findXExtrema(SkScalar* t) const;
     bool findYExtrema(SkScalar* t) const;
diff --git a/src/core/SkGlyph.cpp b/src/core/SkGlyph.cpp
index 35218f4..7e10915 100644
--- a/src/core/SkGlyph.cpp
+++ b/src/core/SkGlyph.cpp
@@ -13,6 +13,8 @@
 #include "src/pathops/SkPathOpsCubic.h"
 #include "src/pathops/SkPathOpsQuad.h"
 
+constexpr SkIPoint SkPackedGlyphID::kXYFieldMask;
+
 SkMask SkGlyph::mask() const {
     // getMetrics had to be called.
     SkASSERT(fMaskFormat != MASK_FORMAT_UNKNOWN);
@@ -123,7 +125,9 @@
         fLeft = from.fLeft;
         fForceBW = from.fForceBW;
         fMaskFormat = from.fMaskFormat;
-        return this->setImage(alloc, from.image());
+
+        // From glyph may not have an image because the glyph is too large.
+        return from.fImage != nullptr && this->setImage(alloc, from.image());
     }
     return false;
 }
diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h
index eef6f60..6065a64 100644
--- a/src/core/SkGlyph.h
+++ b/src/core/SkGlyph.h
@@ -13,6 +13,7 @@
 #include "include/private/SkChecksum.h"
 #include "include/private/SkFixed.h"
 #include "include/private/SkTo.h"
+#include "include/private/SkVx.h"
 #include "src/core/SkMask.h"
 
 class SkArenaAlloc;
@@ -32,9 +33,9 @@
         kSubPixelPosLen = 2u,
 
         // Bit positions
-        kGlyphID   = 0u,
-        kSubPixelY = kGlyphIDLen,
-        kSubPixelX = kGlyphIDLen + kSubPixelPosLen,
+        kSubPixelX = 0u,
+        kGlyphID   = kSubPixelPosLen,
+        kSubPixelY = kGlyphIDLen + kSubPixelPosLen,
         kEndData   = kGlyphIDLen + 2 * kSubPixelPosLen,
 
         // Masks
@@ -47,16 +48,24 @@
         kFixedPointSubPixelPosBits = kFixedPointBinaryPointPos - kSubPixelPosLen,
     };
 
+    static constexpr SkScalar kSubpixelRound = 1.f / (1u << (SkPackedGlyphID::kSubPixelPosLen + 1));
+
+    static constexpr SkIPoint kXYFieldMask{kSubPixelPosMask << kSubPixelX,
+                                           kSubPixelPosMask << kSubPixelY};
+
     constexpr explicit SkPackedGlyphID(SkGlyphID glyphID)
-            : fID{glyphID} { }
+            : fID{(uint32_t)glyphID << kGlyphID} { }
 
     constexpr SkPackedGlyphID(SkGlyphID glyphID, SkFixed x, SkFixed y)
-            : fID {PackIDXY(glyphID, x, y)} {
-        SkASSERT(fID != kImpossibleID);
-    }
+            : fID {PackIDXY(glyphID, x, y)} { }
 
-    constexpr SkPackedGlyphID(SkGlyphID code, SkIPoint pt)
-        : SkPackedGlyphID(code, pt.fX, pt.fY) { }
+    constexpr SkPackedGlyphID(SkGlyphID glyphID, uint32_t x, uint32_t y)
+            : fID {PackIDSubXSubY(glyphID, x, y)} { }
+
+    SkPackedGlyphID(SkGlyphID glyphID, SkPoint pt, SkIPoint mask)
+        : fID{PackIDSkPoint(glyphID, pt, mask)} { }
+
+    constexpr explicit SkPackedGlyphID(uint32_t v) : fID{v & kMaskAll} { }
 
     constexpr SkPackedGlyphID() : fID{kImpossibleID} {}
 
@@ -71,7 +80,7 @@
     }
 
     SkGlyphID glyphID() const {
-        return fID & kGlyphIDMask;
+        return (fID >> kGlyphID) & kGlyphIDMask;
     }
 
     uint32_t value() const {
@@ -92,15 +101,51 @@
 
     SkString dump() const {
         SkString str;
-        str.appendf("code: %d, x: %d, y:%d", glyphID(), getSubXFixed(), getSubYFixed());
+        str.appendf("glyphID: %d, x: %d, y:%d", glyphID(), getSubXFixed(), getSubYFixed());
         return str;
     }
 
 private:
+    static constexpr uint32_t PackIDSubXSubY(SkGlyphID glyphID, uint32_t x, uint32_t y) {
+        SkASSERT(x < (1u << kSubPixelPosLen));
+        SkASSERT(y < (1u << kSubPixelPosLen));
+
+        return (x << kSubPixelX) | (y << kSubPixelY) | (glyphID << kGlyphID);
+    }
+
+    // Assumptions: pt is properly rounded. mask is set for the x or y fields.
+    //
+    // A sub-pixel field is a number on the interval [2^kSubPixel, 2^(kSubPixel + kSubPixelPosLen)).
+    // Where kSubPixel is either kSubPixelX or kSubPixelY. Given a number x on [0, 1) we can
+    // generate a sub-pixel field using:
+    //    sub-pixel-field = x * 2^(kSubPixel + kSubPixelPosLen)
+    //
+    // We can generate the integer sub-pixel field by &-ing the integer part of sub-filed with the
+    // sub-pixel field mask.
+    //    int-sub-pixel-field = int(sub-pixel-field) & (kSubPixelPosMask << kSubPixel)
+    //
+    // The last trick is to extend the range from [0, 1) to [0, 2). The extend range is
+    // necessary because the modulo 1 calculation (pt - floor(pt)) generates numbers on [-1, 1).
+    // This does not round (floor) properly when converting to integer. Adding one to the range
+    // causes truncation and floor to be the same. Coincidentally, masking to produce the field also
+    // removes the +1.
+    static uint32_t PackIDSkPoint(SkGlyphID glyphID, SkPoint pt, SkIPoint mask) {
+        using namespace skvx;
+        using XY = Vec<2, float>;
+        using SubXY = Vec<2, int>;
+        const XY magic = {1.f * (1u << (kSubPixelPosLen + kSubPixelX)),
+                          1.f * (1u << (kSubPixelPosLen + kSubPixelY))};
+        XY pos{pt.x(), pt.y()};
+        XY subPos = (pos - floor(pos)) + 1.0f;
+        SubXY sub = cast<int>(subPos * magic) & SubXY{mask.x(), mask.y()};
+        SkASSERT(sub[0] / (1u << kSubPixelX) < (1u << kSubPixelPosLen));
+        SkASSERT(sub[1] / (1u << kSubPixelY) < (1u << kSubPixelPosLen));
+
+        return (glyphID << kGlyphID) | sub[0] | sub[1];
+    }
+
     static constexpr uint32_t PackIDXY(SkGlyphID glyphID, SkFixed x, SkFixed y) {
-        return (FixedToSub(x) << kSubPixelX)
-             | (FixedToSub(y) << kSubPixelY)
-             | glyphID;
+        return PackIDSubXSubY(glyphID, FixedToSub(x), FixedToSub(y));
     }
 
     static constexpr uint32_t FixedToSub(SkFixed n) {
@@ -119,8 +164,6 @@
 
 class SkGlyph {
 public:
-    static constexpr SkFixed kSubpixelRound = SK_FixedHalf >> SkPackedGlyphID::kSubPixelPosLen;
-
     // SkGlyph() is used for testing.
     constexpr SkGlyph() : fID{SkPackedGlyphID()} { }
     constexpr explicit SkGlyph(SkPackedGlyphID id) : fID{id} { }
diff --git a/src/core/SkGlyphBuffer.cpp b/src/core/SkGlyphBuffer.cpp
index e7ccf57..d44cae6 100644
--- a/src/core/SkGlyphBuffer.cpp
+++ b/src/core/SkGlyphBuffer.cpp
@@ -59,7 +59,7 @@
     matrix.mapPoints(fPositions, positions.data(), positions.size());
 
     // Mask for controlling axis alignment.
-    SkIPoint mask = roundingSpec.ignorePositionMask;
+    SkIPoint mask = roundingSpec.ignorePositionFieldMask;
 
     // Convert glyph ids and positions to packed glyph ids.
     SkZip<const SkGlyphID, const SkPoint> withMappedPos =
@@ -68,9 +68,7 @@
     for (auto t : withMappedPos) {
         SkGlyphID glyphID; SkPoint pos;
         std::tie(glyphID, pos) = t;
-        SkFixed subX = SkScalarToFixed(pos.x()) & mask.x(),
-                subY = SkScalarToFixed(pos.y()) & mask.y();
-        *packedIDCursor++ = SkPackedGlyphID{glyphID, subX, subY};
+        *packedIDCursor++ = SkPackedGlyphID{glyphID, pos, mask};
     }
     SkDEBUGCODE(fPhase = kInput);
 }
diff --git a/src/core/SkGlyphBuffer.h b/src/core/SkGlyphBuffer.h
index f284814..a66a96a 100644
--- a/src/core/SkGlyphBuffer.h
+++ b/src/core/SkGlyphBuffer.h
@@ -8,6 +8,7 @@
 #ifndef SkGlyphBuffer_DEFINED
 #define SkGlyphBuffer_DEFINED
 
+#include "src/core/SkEnumerate.h"
 #include "src/core/SkGlyph.h"
 #include "src/core/SkZip.h"
 
@@ -159,23 +160,6 @@
         return SkZip<SkGlyphVariant, SkPoint>{fInputSize, fMultiBuffer, fPositions};
     }
 
-    // DO NOT USE. This is only used to work around how prepareForDrawing works.
-    void flipDrawableToInput() {
-        SkASSERT(fPhase == kProcess);
-        fInputSize = fDrawableSize;
-        fDrawableSize = 0;
-        SkDEBUGCODE(fPhase = kInput);
-    }
-
-    // DO NOT USE. This is only used to work around how prepareForDrawing works.
-    void push_back(size_t from) {
-        SkASSERT(fPhase == kProcess);
-        SkASSERT(fDrawableSize <= from);
-        fPositions[fDrawableSize] = fPositions[from];
-        fMultiBuffer[fDrawableSize] = fMultiBuffer[from];
-        fDrawableSize++;
-    }
-
     // Store the glyph in the next drawable slot, using the position information located at index
     // from.
     void push_back(SkGlyph* glyph, size_t from) {
@@ -205,6 +189,13 @@
 
     void reset();
 
+    template <typename Fn>
+    void forEachGlyphID(Fn&& fn) {
+        for (auto [i, packedID, pos] : SkMakeEnumerate(this->input())) {
+            fn(i, packedID.packedID(), pos);
+        }
+    }
+
 private:
     size_t fMaxSize{0};
     size_t fInputSize{0};
diff --git a/src/core/SkGlyphRunPainter.cpp b/src/core/SkGlyphRunPainter.cpp
index 6934168..20228fd 100644
--- a/src/core/SkGlyphRunPainter.cpp
+++ b/src/core/SkGlyphRunPainter.cpp
@@ -82,6 +82,10 @@
         const BitmapDevicePainter* bitmapDevice) {
     ScopedBuffers _ = this->ensureBuffers(glyphRunList);
 
+    // TODO: fStrikeCache is only used for GPU, and some compilers complain about it during the no
+    //  gpu build. Remove when SkGlyphRunListPainter is split into GPU and CPU version.
+    (void)fStrikeCache;
+
     const SkPaint& runPaint = glyphRunList.paint();
     // The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise,
     // convert the lcd text into A8 text. The props communicates this to the scaler.
@@ -92,7 +96,7 @@
     SkPoint origin = glyphRunList.origin();
     for (auto& glyphRun : glyphRunList) {
         const SkFont& runFont = glyphRun.font();
-
+#ifdef SK_SUPPORT_LEGACY_CPU_EMOJI
         if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
 
             SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
@@ -119,90 +123,42 @@
             strike->prepareForDrawingMasksCPU(&fDrawable);
             bitmapDevice->paintMasks(&fDrawable, runPaint);
         }
-    }
-}
+#else
+        fRejects.setSource(glyphRun.source());
 
-// Getting glyphs to the screen in a fallback situation can be complex. Here is the set of
-// transformations that have to happen. Normally, they would all be accommodated by the font
-// scaler, but the atlas has an upper limit to the glyphs it can handle. So the GPU is used to
-// make up the difference from the smaller atlas size to the larger size needed by the final
-// transform. Here are the transformations that are applied.
-//
-// final transform = [view matrix] * [text scale] * [text size]
-//
-// There are three cases:
-// * Go Fast - view matrix is scale and translate, and all the glyphs are small enough
-//   Just scale the positions, and have the glyph cache handle the view matrix transformation.
-//   The text scale is 1.
-// * It's complicated - view matrix is not scale and translate, and the glyphs are small enough
-//   The glyph cache does not handle the view matrix, but stores the glyphs at the text size
-//   specified by the run paint. The GPU handles the rotation, etc. specified by the view matrix.
-//   The text scale is 1.
-// * Too big - The glyphs are too big to fit in the atlas
-//   Reduce the text size so the glyphs will fit in the atlas, but don't apply any
-//   transformations from the view matrix. Calculate a text scale based on that reduction. This
-//   scale factor is used to increase the size of the destination rectangles. The destination
-//   rectangles are then scaled, rotated, etc. by the GPU using the view matrix.
-void SkGlyphRunListPainter::processARGBFallback(SkScalar maxSourceGlyphDimension,
-                                                const SkPaint& runPaint,
-                                                const SkFont& runFont,
-                                                SkPoint origin,
-                                                const SkMatrix& viewMatrix,
-                                                SkGlyphRunPainterInterface* process) {
-    // if maxSourceGlyphDimension then no pixels will change.
-    if (maxSourceGlyphDimension == 0) { return; }
+        if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
 
-    SkScalar maxScale = viewMatrix.getMaxScale();
+            SkStrikeSpec strikeSpec = SkStrikeSpec::MakePath(
+                    runFont, runPaint, props, fScalerContextFlags);
 
-    // This is a linear estimate of the longest dimension among all the glyph widths and heights.
-    SkScalar conservativeMaxGlyphDimension = maxSourceGlyphDimension * maxScale;
+            auto strike = strikeSpec.findOrCreateExclusiveStrike();
 
-    // If the situation that the matrix is simple, and all the glyphs are small enough. Go fast!
-    // N.B. If the matrix has scale, that will be reflected in the strike through the viewMatrix
-    // in the useFastPath case.
-    bool useDeviceCache =
-            viewMatrix.isScaleTranslate()
-            && conservativeMaxGlyphDimension <= SkStrikeCommon::kSkSideTooBigForAtlas;
+            fDrawable.startSource(fRejects.source(), origin);
+            strike->prepareForPathDrawing(&fDrawable, &fRejects);
+            fRejects.flipRejectsToSource();
 
-    // A scaled and translated transform is the common case, and is handled directly in fallback.
-    // Even if the transform is scale and translate, fallback must be careful to use glyphs that
-    // fit in the atlas. If a glyph will not fit in the atlas, then the general transform case is
-    // used to render the glyphs.
-    if (useDeviceCache) {
-        SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
-                runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
+            // The paint we draw paths with must have the same anti-aliasing state as the runFont
+            // allowing the paths to have the same edging as the glyph masks.
+            SkPaint pathPaint = runPaint;
+            pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
 
-        SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
+            bitmapDevice->paintPaths(&fDrawable, strikeSpec.strikeToSourceRatio(), pathPaint);
+        }
+        if (!fRejects.source().empty()) {
+            SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
+                    runFont, runPaint, props, fScalerContextFlags, deviceMatrix);
 
-        fDrawable.startDevice(fRejects.source(), origin, viewMatrix, strike->roundingSpec());
+            auto strike = strikeSpec.findOrCreateExclusiveStrike();
 
-        strike->prepareForDrawing(
-                SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
-
-        if (process) {
-            process->processDeviceFallback(fDrawable.drawable(), strikeSpec);
+            fDrawable.startDevice(fRejects.source(), origin, deviceMatrix, strike->roundingSpec());
+            strike->prepareForDrawingMasksCPU(&fDrawable);
+            bitmapDevice->paintMasks(&fDrawable, runPaint);
         }
 
-    } else {
-        // If the matrix is complicated or if scaling is used to fit the glyphs in the cache,
-        // then this case is used.
+        // TODO: have the mask stage above reject the glyphs that are too big, and handle the
+        //  rejects in a more sophisticated stage.
+#endif
 
-        SkStrikeSpec strikeSpec = SkStrikeSpec::MakeSourceFallback(
-                runFont, runPaint, fDeviceProps, fScalerContextFlags, maxSourceGlyphDimension);
-
-        SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
-
-        fDrawable.startSource(fRejects.source(), origin);
-
-        strike->prepareForDrawing(
-                SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
-
-        if (process) {
-            process->processSourceFallback(
-                    fDrawable.drawable(),
-                    strikeSpec,
-                    viewMatrix.hasPerspective());
-        }
     }
 }
 
@@ -229,41 +185,16 @@
         bool usePaths =
                 useSDFT ? false : SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, viewMatrix);
 
-        if (process) {
-            process->startRun(glyphRun, useSDFT);
-        }
-
         if (!useSDFT && !usePaths) {
-            // Mask case
-            SkStrikeSpec strikeSpec =
-                    SkStrikeSpec::MakeMask(runFont, runPaint,
-                                           fDeviceProps, fScalerContextFlags, viewMatrix);
+            // Process masks - this should be the 99.99% case.
+
+            SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
+                    runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
 
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
             fDrawable.startDevice(fRejects.source(), origin, viewMatrix, strike->roundingSpec());
-
-            strike->prepareForDrawing(
-                    SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
-
-            // Sort glyphs into the three bins: mask (fGlyphPos), path (fPaths), and fallback.
-            fDrawable.flipDrawableToInput();
-            for (auto t : SkMakeEnumerate(fDrawable.input())) {
-                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
-                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
-                SkGlyph* glyph = glyphVariant.glyph();
-                if (!glyph->isEmpty()) {
-                    // Does the glyph have work to do or is the code able to position the glyph?
-                    if (!SkScalarsAreFinite(pos.x(), pos.y())) {
-                        // Do nothing;
-                    } else if (SkStrikeForGPU::CanDrawAsMask(*glyph)) {
-                        fDrawable.push_back(glyph, i);
-                    } else {
-                        fRejects.reject(i);
-                    }
-                }
-            }
-
+            strike->prepareForMaskDrawing(&fDrawable, &fRejects);
             fRejects.flipRejectsToSource();
 
             if (process) {
@@ -272,51 +203,23 @@
                 process->processDeviceMasks(fDrawable.drawable(), strikeSpec);
             }
         } else if (useSDFT) {
-            // SDFT case
+            // Process SDFT - This should be the .009% case.
             SkScalar minScale, maxScale;
             SkStrikeSpec strikeSpec;
             std::tie(strikeSpec, minScale, maxScale) =
-                    SkStrikeSpec::MakeSDFT(
-                            runFont, runPaint,fDeviceProps, viewMatrix, options);
+                    SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, viewMatrix, options);
 
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
             fDrawable.startSource(fRejects.source(), origin);
-            strike->prepareForDrawing(SkStrikeCommon::kSkSideTooBigForAtlas, &fDrawable);
-
-            fDrawable.flipDrawableToInput();
-            for (auto t : SkMakeEnumerate(fDrawable.input())) {
-                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
-                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
-                SkGlyph* glyph = glyphVariant.glyph();
-
-                // The SDF scaler context system ensures that a glyph is empty, kSDF_Format, or
-                // kARGB32_Format. The following if statements use this assumption.
-                SkASSERT(glyph->maskFormat() == SkMask::kSDF_Format
-                         || glyph->isColor()
-                         || glyph->isEmpty());
-
-                if (!glyph->isEmpty()) {
-                    if (SkStrikeForGPU::CanDrawAsSDFT(*glyph)) {
-                        // SDF mask will work.
-                        fDrawable.push_back(glyph, i);
-                    }  else {
-                        // If no path, or it is color, then fallback.
-                        fRejects.reject(i);
-                    }
-                }
-            }
-
+            strike->prepareForSDFTDrawing(&fDrawable, &fRejects);
             fRejects.flipRejectsToSource();
 
             if (process) {
-                bool hasWCoord =
-                        viewMatrix.hasPerspective() || options.fDistanceFieldVerticesAlwaysHaveW;
-
                 // processSourceSDFT must be called even if there are no glyphs to make sure runs
                 // are set correctly.
                 process->processSourceSDFT(
-                        fDrawable.drawable(), strikeSpec, runFont, minScale, maxScale, hasWCoord);
+                        fDrawable.drawable(), strikeSpec, runFont, minScale, maxScale);
             }
         }
 
@@ -332,23 +235,7 @@
             SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
 
             fDrawable.startSource(fRejects.source(), origin);
-            strike->prepareForDrawing(0, &fDrawable);
-
-            fDrawable.flipDrawableToInput();
-            for (auto t : SkMakeEnumerate(fDrawable.input())) {
-                size_t i; SkGlyphVariant glyphVariant; SkPoint pos;
-                std::forward_as_tuple(i, std::tie(glyphVariant, pos)) = t;
-                const SkGlyph& glyph = *glyphVariant.glyph();
-                if (!glyph.isEmpty()) {
-                    if (SkStrikeForGPU::CanDrawAsPath(glyph)) {
-                        // Place paths in fGlyphPos
-                        fDrawable.push_back(glyph.path(), i);
-                    } else {
-                        fRejects.reject(i, glyph.maxDimension());
-                    }
-                }
-            }
-
+            strike->prepareForPathDrawing(&fDrawable, &fRejects);
             fRejects.flipRejectsToSource();
             maxDimensionInSourceSpace =
                     fRejects.rejectedMaxDimension() * strikeSpec.strikeToSourceRatio();
@@ -356,14 +243,89 @@
             if (process) {
                 // processSourcePaths must be called even if there are no glyphs to make sure runs
                 // are set correctly.
-                process->processSourcePaths(fDrawable.drawable(), strikeSpec);
+                process->processSourcePaths(fDrawable.drawable(), runFont, strikeSpec);
             }
         }
 
-        // Handle fallback for all cases.
-        if (!fRejects.source().empty()) {
-            this->processARGBFallback(
-                    maxDimensionInSourceSpace, runPaint, runFont, origin, viewMatrix, process);
+        // Getting glyphs to the screen in a fallback situation can be complex. Here is the set of
+        // transformations that have to happen. Normally, they would all be accommodated by the font
+        // scaler, but the atlas has an upper limit to the glyphs it can handle. So the GPU is used
+        // to make up the difference from the smaller atlas size to the larger size needed by the
+        // final transform. Here are the transformations that are applied.
+        //
+        // final transform = [view matrix] * [text scale] * [text size]
+        //
+        // There are three cases:
+        // * Go Fast - view matrix is scale and translate, and all the glyphs are small enough
+        //   Just scale the positions, and have the glyph cache handle the view matrix
+        //   transformation.
+        //   The text scale is 1.
+        // * It's complicated - view matrix is not scale and translate, and the glyphs are small
+        //   enough The glyph cache does not handle the view matrix, but stores the glyphs at the
+        //   text size specified by the run paint. The GPU handles the rotation, etc. specified
+        //   by the view matrix.
+        //   The text scale is 1.
+        // * Too big - The glyphs are too big to fit in the atlas
+        //   Reduce the text size so the glyphs will fit in the atlas, but don't apply any
+        //   transformations from the view matrix. Calculate a text scale based on that reduction.
+        //   This scale factor is used to increase the size of the destination rectangles. The
+        //   destination rectangles are then scaled, rotated, etc. by the GPU using the view matrix.
+
+        if (!fRejects.source().empty() && maxDimensionInSourceSpace != 0) {
+
+            SkScalar maxScale = viewMatrix.getMaxScale();
+
+            // This is a linear estimate of the longest dimension among all the glyph widths and
+            // heights.
+            SkScalar conservativeMaxGlyphDimension = maxDimensionInSourceSpace * maxScale;
+
+            // If the situation that the matrix is simple, and all the glyphs are small enough.
+            // Go fast!
+            // N.B. If the matrix has scale, that will be reflected in the strike through the
+            // viewMatrix in the useFastPath case.
+            bool useDeviceCache =
+                    viewMatrix.isScaleTranslate()
+                    && conservativeMaxGlyphDimension <= SkStrikeCommon::kSkSideTooBigForAtlas;
+
+            // A scaled and translated transform is the common case, and is handled directly in
+            // fallback. Even if the transform is scale and translate, fallback must be careful
+            // to use glyphs that fit in the atlas. If a glyph will not fit in the atlas, then
+            // the general transform case is used to render the glyphs.
+            if (useDeviceCache) {
+                SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
+                        runFont, runPaint, fDeviceProps, fScalerContextFlags, viewMatrix);
+
+                SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
+
+                fDrawable.startDevice(
+                        fRejects.source(), origin, viewMatrix, strike->roundingSpec());
+
+                strike->prepareForMaskDrawing(&fDrawable, &fRejects);
+                fRejects.flipRejectsToSource();
+                SkASSERT(fRejects.source().empty());
+
+                if (process) {
+                    process->processDeviceMasks(fDrawable.drawable(), strikeSpec);
+                }
+            } else {
+                // If the matrix is complicated or if scaling is used to fit the glyphs in the
+                // atlas, then this case is used.
+
+                SkStrikeSpec strikeSpec = SkStrikeSpec::MakeSourceFallback(
+                        runFont, runPaint, fDeviceProps,
+                        fScalerContextFlags, maxDimensionInSourceSpace);
+
+                SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
+
+                fDrawable.startSource(fRejects.source(), origin);
+                strike->prepareForMaskDrawing(&fDrawable, &fRejects);
+                fRejects.flipRejectsToSource();
+                SkASSERT(fRejects.source().empty());
+
+                if (process) {
+                    process->processSourceMasks(fDrawable.drawable(), strikeSpec);
+                }
+            }
         }
     }  // For all glyph runs
 }
@@ -448,6 +410,7 @@
         cacheBlob = textBlobCache->find(key);
     }
 
+    bool forceW = fOptions.fDistanceFieldVerticesAlwaysHaveW;
     if (cacheBlob) {
         if (cacheBlob->mustRegenerate(listPaint, glyphRunList.anyRunsSubpixelPositioned(),
                                       blurRec, viewMatrix, origin.x(),origin.y())) {
@@ -456,21 +419,21 @@
             // but we'd have to clear the subrun information
             textBlobCache->remove(cacheBlob.get());
             cacheBlob = textBlobCache->makeCachedBlob(
-                    glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
+                    glyphRunList, key, blurRec, listPaint, forceW, color, grStrikeCache);
             cacheBlob->generateFromGlyphRunList(
                     *context->priv().caps()->shaderCaps(), fOptions,
-                    listPaint, scalerContextFlags, viewMatrix, props,
+                    listPaint, viewMatrix, props,
                     glyphRunList, target->glyphPainter());
         } else {
             textBlobCache->makeMRU(cacheBlob.get());
 
             if (CACHE_SANITY_CHECK) {
                 sk_sp<GrTextBlob> sanityBlob(textBlobCache->makeBlob(
-                        glyphRunList, color, grStrikeCache));
+                        glyphRunList, forceW, color, grStrikeCache));
                 sanityBlob->setupKey(key, blurRec, listPaint);
                 cacheBlob->generateFromGlyphRunList(
                         *context->priv().caps()->shaderCaps(), fOptions,
-                        listPaint, scalerContextFlags, viewMatrix, props, glyphRunList,
+                        listPaint, viewMatrix, props, glyphRunList,
                         target->glyphPainter());
                 GrTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
             }
@@ -478,13 +441,13 @@
     } else {
         if (canCache) {
             cacheBlob = textBlobCache->makeCachedBlob(
-                    glyphRunList, key, blurRec, listPaint, color, grStrikeCache);
+                    glyphRunList, key, blurRec, listPaint, forceW, color, grStrikeCache);
         } else {
-            cacheBlob = textBlobCache->makeBlob(glyphRunList, color, grStrikeCache);
+            cacheBlob = textBlobCache->makeBlob(glyphRunList, forceW, color, grStrikeCache);
         }
         cacheBlob->generateFromGlyphRunList(
                 *context->priv().caps()->shaderCaps(), fOptions, listPaint,
-                scalerContextFlags, viewMatrix, props, glyphRunList,
+                viewMatrix, props, glyphRunList,
                 target->glyphPainter());
     }
 
@@ -492,97 +455,9 @@
                      clip, viewMatrix, origin.x(), origin.y());
 }
 
-void GrTextBlob::SubRun::appendGlyph(GrGlyph* glyph, SkRect dstRect) {
-
-    this->joinGlyphBounds(dstRect);
-
-    GrTextBlob* blob = fRun->fBlob;
-
-    bool hasW = this->hasWCoord();
-    // glyphs drawn in perspective must always have a w coord.
-    SkASSERT(hasW || !blob->fInitialViewMatrix.hasPerspective());
-    auto maskFormat = this->maskFormat();
-    size_t vertexStride = GetVertexStride(maskFormat, hasW);
-
-    intptr_t vertex = reinterpret_cast<intptr_t>(blob->fVertices + fVertexEndIndex);
-
-    // We always write the third position component used by SDFs. If it is unused it gets
-    // overwritten. Similarly, we always write the color and the blob will later overwrite it
-    // with texture coords if it is unused.
-    size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
-    // V0
-    *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
-    *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
-    vertex += vertexStride;
-
-    // V1
-    *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
-    *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
-    vertex += vertexStride;
-
-    // V2
-    *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
-    *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
-    vertex += vertexStride;
-
-    // V3
-    *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
-    *reinterpret_cast<GrColor*>(vertex + colorOffset) = fColor;
-
-    fVertexEndIndex += vertexStride * kVerticesPerGlyph;
-    blob->fGlyphs[fGlyphEndIndex++] = glyph;
-}
-
-void GrTextBlob::Run::switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
-                                                         const sk_sp<GrTextStrike>& strike,
-                                                         const SkRect& destRect,
-                                                         bool needsTransform) {
-    GrMaskFormat format = glyph->fMaskFormat;
-
-    SubRun* subRun = &fSubRunInfo.back();
-    if (fInitialized && subRun->maskFormat() != format) {
-        subRun = pushBackSubRun(fStrikeSpec, fColor);
-        subRun->setStrike(strike);
-    } else if (!fInitialized) {
-        subRun->setStrike(strike);
-    }
-
-    fInitialized = true;
-    subRun->setMaskFormat(format);
-    subRun->setNeedsTransform(needsTransform);
-    subRun->appendGlyph(glyph, destRect);
-}
-
-void GrTextBlob::Run::appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
-                                             const SkGlyph& skGlyph, SkPoint origin) {
-    if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
-
-        SkRect glyphRect = glyph->destRect(origin);
-
-        if (!glyphRect.isEmpty()) {
-            this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, false);
-        }
-    }
-}
-
-void GrTextBlob::Run::appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
-                                             const SkGlyph& skGlyph,
-                                             SkPoint origin,
-                                             SkScalar textScale) {
-    if (GrGlyph* glyph = strike->getGlyph(skGlyph)) {
-
-        SkRect glyphRect = glyph->destRect(origin, textScale);
-
-        if (!glyphRect.isEmpty()) {
-            this->switchSubRunIfNeededAndAppendGlyph(glyph, strike, glyphRect, true);
-        }
-    }
-}
-
 void GrTextBlob::generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
                                           const GrTextContext::Options& options,
                                           const SkPaint& paint,
-                                          SkScalerContextFlags scalerContextFlags,
                                           const SkMatrix& viewMatrix,
                                           const SkSurfaceProps& props,
                                           const SkGlyphRunList& glyphRunList,
@@ -600,43 +475,182 @@
                                       this);
 }
 
-GrTextBlob::Run* GrTextBlob::currentRun() {
-    return &fRuns[fRunCount - 1];
+GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
+                           GrMaskFormat format, const GrTextBlob::SubRunBufferSpec& bufferSpec,
+                           sk_sp<GrTextStrike>&& grStrike)
+        : fType{type}
+        , fBlob{textBlob}
+        , fMaskFormat{format}
+        , fGlyphStartIndex{std::get<0>(bufferSpec)}
+        , fGlyphEndIndex{std::get<1>(bufferSpec)}
+        , fVertexStartIndex{std::get<2>(bufferSpec)}
+        , fVertexEndIndex{std::get<3>(bufferSpec)}
+        , fStrikeSpec{strikeSpec}
+        , fStrike{grStrike}
+        , fColor{textBlob->fColor}
+        , fX{textBlob->fInitialX}
+        , fY{textBlob->fInitialY}
+        , fCurrentViewMatrix{textBlob->fInitialViewMatrix} {
+    SkASSERT(type != kTransformedPath);
 }
 
-void GrTextBlob::startRun(const SkGlyphRun& glyphRun, bool useSDFT) {
-    if (useSDFT) {
-        this->setHasDistanceField();
+GrTextBlob::SubRun::SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec)
+        : fType{kTransformedPath}
+        , fBlob{textBlob}
+        , fMaskFormat{kA8_GrMaskFormat}
+        , fGlyphStartIndex{0}
+        , fGlyphEndIndex{0}
+        , fVertexStartIndex{0}
+        , fVertexEndIndex{0}
+        , fStrikeSpec{strikeSpec}
+        , fStrike{nullptr}
+        , fColor{textBlob->fColor}
+        , fPaths{} { }
+
+class GrTextBlob::SubRun* GrTextBlob::makeSubRun(SubRunType type,
+                                                 const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                                                 const SkStrikeSpec& strikeSpec,
+                                                 GrMaskFormat format) {
+    bool hasW = this->hasW(type);
+    uint32_t glyphsStart = fGlyphsCursor;
+    fGlyphsCursor += drawables.size();
+    uint32_t glyphsEnd = fGlyphsCursor;
+    size_t verticesStart = fVerticesCursor;
+    fVerticesCursor += drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
+    size_t verticesEnd = fVerticesCursor;
+
+    SubRunBufferSpec bufferSpec = std::make_tuple(
+            glyphsStart, glyphsEnd, verticesStart, verticesEnd);
+
+    sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
+
+    SubRun& subRun = fSubRuns.emplace_back(
+            type, this, strikeSpec, format, bufferSpec, std::move(grStrike));
+
+    subRun.appendGlyphs(drawables);
+
+    return &subRun;
+}
+
+void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
+    GrTextStrike* grStrike = fStrike.get();
+    SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
+    uint32_t glyphCursor = fGlyphStartIndex;
+    size_t vertexCursor = fVertexStartIndex;
+    bool hasW = this->hasW();
+    GrColor color = this->color();
+    // glyphs drawn in perspective must always have a w coord.
+    SkASSERT(hasW || !fBlob->fInitialViewMatrix.hasPerspective());
+    size_t vertexStride = GetVertexStride(fMaskFormat, hasW);
+    // We always write the third position component used by SDFs. If it is unused it gets
+    // overwritten. Similarly, we always write the color and the blob will later overwrite it
+    // with texture coords if it is unused.
+    size_t colorOffset = hasW ? sizeof(SkPoint3) : sizeof(SkPoint);
+    for (auto t : drawables) {
+        SkGlyph* skGlyph; SkPoint pos;
+        std::tie(skGlyph, pos) = t;
+
+        GrGlyph* grGlyph = grStrike->getGlyph(*skGlyph);
+        // Only floor the device coordinates.
+        SkRect dstRect;
+        if (!this->needsTransform()) {
+            pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
+            dstRect = grGlyph->destRect(pos);
+        } else {
+            dstRect = grGlyph->destRect(pos, strikeToSource);
+        }
+
+        this->joinGlyphBounds(dstRect);
+
+        intptr_t vertex = reinterpret_cast<intptr_t>(fBlob->fVertices + vertexCursor);
+
+        // V0
+        *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fTop, 1.f};
+        *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+        vertex += vertexStride;
+
+        // V1
+        *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fLeft, dstRect.fBottom, 1.f};
+        *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+        vertex += vertexStride;
+
+        // V2
+        *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fTop, 1.f};
+        *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+        vertex += vertexStride;
+
+        // V3
+        *reinterpret_cast<SkPoint3*>(vertex) = {dstRect.fRight, dstRect.fBottom, 1.f};
+        *reinterpret_cast<GrColor*>(vertex + colorOffset) = color;
+
+        vertexCursor += vertexStride * kVerticesPerGlyph;
+        fBlob->fGlyphs[glyphCursor++] = grGlyph;
     }
-    Run* run = this->pushBackRun();
-    run->setRunFontAntiAlias(glyphRun.font().hasSomeAntiAliasing());
+    SkASSERT(glyphCursor == fGlyphEndIndex);
+    SkASSERT(vertexCursor == fVertexEndIndex);
+}
+
+void GrTextBlob::addSingleMaskFormat(
+        SubRunType type,
+        const SkZip<SkGlyphVariant, SkPoint>& drawables,
+        const SkStrikeSpec& strikeSpec,
+        GrMaskFormat format) {
+    this->makeSubRun(type, drawables, strikeSpec, format);
+}
+
+void GrTextBlob::addMultiMaskFormat(
+        SubRunType type,
+        const SkZip<SkGlyphVariant, SkPoint>& drawables,
+        const SkStrikeSpec& strikeSpec) {
+    this->setHasBitmap();
+    if (drawables.empty()) { return; }
+
+    SkGlyph* glyph;
+    std::tie(glyph, std::ignore) = drawables[0];
+    GrMaskFormat format = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
+    size_t startIndex = 0;
+    for (size_t i = 1; i < drawables.size(); i++) {
+        std::tie(glyph, std::ignore) = drawables[i];
+        GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
+        if (format != nextFormat) {
+            auto sameFormat = drawables.subspan(startIndex, i - startIndex);
+            this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
+            format = nextFormat;
+            startIndex = i;
+        }
+    }
+    auto sameFormat = drawables.last(drawables.size() - startIndex);
+    this->addSingleMaskFormat(type, sameFormat, strikeSpec, format);
+}
+
+void GrTextBlob::addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                         const SkStrikeSpec& strikeSpec,
+                         const SkFont& runFont,
+                         SkScalar minScale,
+                         SkScalar maxScale) {
+    this->setHasDistanceField();
+    this->setMinAndMaxScale(minScale, maxScale);
+
+    SubRun* subRun = this->makeSubRun(kTransformedSDFT, drawables, strikeSpec, kA8_GrMaskFormat);
+    subRun->setUseLCDText(runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias);
+    subRun->setAntiAliased(runFont.hasSomeAntiAliasing());
 }
 
 void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                     const SkStrikeSpec& strikeSpec) {
-    Run* run = this->currentRun();
-    this->setHasBitmap();
-    run->setupFont(strikeSpec);
-    sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    for (auto t : drawables) {
-        SkGlyph* glyph; SkPoint pos;
-        std::tie(glyph, pos) = t;
-        SkPoint pt{SkScalarFloorToScalar(pos.fX),
-                   SkScalarFloorToScalar(pos.fY)};
-        run->appendDeviceSpaceGlyph(currStrike, *glyph, pt);
-    }
+    this->addMultiMaskFormat(kDirectMask, drawables, strikeSpec);
 }
 
 void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                                    const SkFont& runFont,
                                     const SkStrikeSpec& strikeSpec) {
-    Run* run = this->currentRun();
     this->setHasBitmap();
-    // FIXME: why was this ever called?
-    //run->setupFont(strikeSpec);
+    SubRun& subRun = fSubRuns.emplace_back(this, strikeSpec);
+    subRun.setAntiAliased(runFont.hasSomeAntiAliasing());
     for (auto t : drawables) {
         const SkPath* path; SkPoint pos;
         std::tie(path, pos) = t;
-        run->appendPathGlyph(*path, pos, strikeSpec.strikeToSourceRatio(), false);
+        subRun.fPaths.emplace_back(*path, pos);
     }
 }
 
@@ -644,58 +658,13 @@
                                    const SkStrikeSpec& strikeSpec,
                                    const SkFont& runFont,
                                    SkScalar minScale,
-                                   SkScalar maxScale,
-                                   bool hasWCoord) {
-
-    Run* run = this->currentRun();
-    run->setSubRunHasDistanceFields(
-            runFont.getEdging() == SkFont::Edging::kSubpixelAntiAlias,
-            runFont.hasSomeAntiAliasing(),
-            hasWCoord);
-    this->setMinAndMaxScale(minScale, maxScale);
-    run->setupFont(strikeSpec);
-    sk_sp<GrTextStrike> currStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    for (auto t : drawables) {
-        SkGlyph* glyph; SkPoint pos;
-        std::tie(glyph, pos) = t;
-        run->appendSourceSpaceGlyph(currStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
-    }
+                                   SkScalar maxScale) {
+    this->addSDFT(drawables, strikeSpec, runFont, minScale, maxScale);
 }
 
-void GrTextBlob::processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                                       const SkStrikeSpec& strikeSpec,
-                                       bool hasW) {
-    Run* run = this->currentRun();
-
-    auto subRun = run->initARGBFallback();
-    sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    subRun->setStrike(grStrike);
-    subRun->setHasWCoord(hasW);
-
-    this->setHasBitmap();
-    run->setupFont(strikeSpec);
-    for (auto t : drawables) {
-        SkGlyph* glyph; SkPoint pos;
-        std::tie(glyph, pos) = t;
-        run->appendSourceSpaceGlyph(grStrike, *glyph, pos, strikeSpec.strikeToSourceRatio());
-    }
-}
-
-void GrTextBlob::processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                                       const SkStrikeSpec& strikeSpec) {
-    Run* run = this->currentRun();
-    this->setHasBitmap();
-    sk_sp<GrTextStrike> grStrike = strikeSpec.findOrCreateGrStrike(fStrikeCache);
-    auto subRun = run->initARGBFallback();
-    run->setupFont(strikeSpec);
-    subRun->setStrike(grStrike);
-    for (auto t : drawables) {
-        SkGlyph* glyph; SkPoint pos;
-        std::tie(glyph, pos) = t;
-        SkPoint pt{SkScalarFloorToScalar(pos.fX),
-                   SkScalarFloorToScalar(pos.fY)};
-        run->appendDeviceSpaceGlyph(grStrike, *glyph, pt);
-    }
+void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                                    const SkStrikeSpec& strikeSpec) {
+    this->addMultiMaskFormat(kTransformedMask, drawables, strikeSpec);
 }
 
 #if GR_TEST_UTILS
@@ -733,16 +702,14 @@
     auto glyphRunList = builder.useGlyphRunList();
     sk_sp<GrTextBlob> blob;
     if (!glyphRunList.empty()) {
-        blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, color, strikeCache);
-        // Use the text and textLen below, because we don't want to mess with the paint.
-        SkScalerContextFlags scalerContextFlags = ComputeScalerContextFlags(rtc->colorInfo());
+        blob = direct->priv().getTextBlobCache()->makeBlob(glyphRunList, false, color, strikeCache);
         blob->generateFromGlyphRunList(
                 *context->priv().caps()->shaderCaps(), textContext->fOptions,
-                skPaint, scalerContextFlags, viewMatrix, surfaceProps,
+                skPaint, viewMatrix, surfaceProps,
                 glyphRunList, rtc->textTarget()->glyphPainter());
     }
 
-    return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, skPaint, filteredColor, surfaceProps,
+    return blob->test_makeOp(textLen, viewMatrix, x, y, skPaint, filteredColor, surfaceProps,
                              textContext->dfAdjustTable(), rtc->textTarget());
 }
 
@@ -763,14 +730,13 @@
     if (!isSubpixel) {
         return {SK_ScalarHalf, SK_ScalarHalf};
     } else {
-        static constexpr SkScalar kSubpixelRounding = SkFixedToScalar(SkGlyph::kSubpixelRound);
         switch (axisAlignment) {
             case kX_SkAxisAlignment:
-                return {kSubpixelRounding, SK_ScalarHalf};
+                return {SkPackedGlyphID::kSubpixelRound, SK_ScalarHalf};
             case kY_SkAxisAlignment:
-                return {SK_ScalarHalf, kSubpixelRounding};
+                return {SK_ScalarHalf, SkPackedGlyphID::kSubpixelRound};
             case kNone_SkAxisAlignment:
-                return {kSubpixelRounding, kSubpixelRounding};
+                return {SkPackedGlyphID::kSubpixelRound, SkPackedGlyphID::kSubpixelRound};
         }
     }
 
@@ -784,8 +750,16 @@
                           (!isSubpixel || axisAlignment == kX_SkAxisAlignment) ? 0 : ~0);
 }
 
-SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(bool isSubpixel,
-                                                         SkAxisAlignment axisAlignment)
-        : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
-        , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)} {
+SkIPoint SkGlyphPositionRoundingSpec::IgnorePositionFieldMask(bool isSubpixel,
+                                                              SkAxisAlignment axisAlignment) {
+    SkIPoint ignoreMask = IgnorePositionMask(isSubpixel, axisAlignment);
+    SkIPoint answer{ignoreMask.x() & SkPackedGlyphID::kXYFieldMask.x(),
+                    ignoreMask.y() & SkPackedGlyphID::kXYFieldMask.y()};
+    return answer;
 }
+
+SkGlyphPositionRoundingSpec::SkGlyphPositionRoundingSpec(
+        bool isSubpixel,SkAxisAlignment axisAlignment)
+    : halfAxisSampleFreq{HalfAxisSampleFreq(isSubpixel, axisAlignment)}
+    , ignorePositionMask{IgnorePositionMask(isSubpixel, axisAlignment)}
+    , ignorePositionFieldMask {IgnorePositionFieldMask(isSubpixel, axisAlignment)}{ }
diff --git a/src/core/SkGlyphRunPainter.h b/src/core/SkGlyphRunPainter.h
index 954ffb0..5866a9f 100644
--- a/src/core/SkGlyphRunPainter.h
+++ b/src/core/SkGlyphRunPainter.h
@@ -35,10 +35,12 @@
     SkGlyphPositionRoundingSpec(bool isSubpixel, SkAxisAlignment axisAlignment);
     const SkVector halfAxisSampleFreq;
     const SkIPoint ignorePositionMask;
+    const SkIPoint ignorePositionFieldMask;
 
 private:
     static SkVector HalfAxisSampleFreq(bool isSubpixel, SkAxisAlignment axisAlignment);
     static SkIPoint IgnorePositionMask(bool isSubpixel, SkAxisAlignment axisAlignment);
+    static SkIPoint IgnorePositionFieldMask(bool isSubpixel, SkAxisAlignment axisAlignment);
 };
 
 class SkStrikeCommon {
@@ -103,20 +105,6 @@
     // TODO: Remove once I can hoist ensureBuffers above the list for loop in all cases.
     ScopedBuffers SK_WARN_UNUSED_RESULT ensureBuffers(const SkGlyphRun& glyphRun);
 
-    /**
-     *  @param fARGBPositions in source space
-     *  @param fARGBGlyphsIDs the glyphs to process
-     *  @param fGlyphPos used as scratch space
-     *  @param maxSourceGlyphDimension the longest dimension of any glyph as if all fARGBGlyphsIDs
-     *                                 were drawn in source space (as if viewMatrix were identity)
-     */
-    void processARGBFallback(SkScalar maxSourceGlyphDimension,
-                             const SkPaint& runPaint,
-                             const SkFont& runFont,
-                             SkPoint origin,
-                             const SkMatrix& viewMatrix,
-                             SkGlyphRunPainterInterface* process);
-
     // The props as on the actual device.
     const SkSurfaceProps fDeviceProps;
     // The props for when the bitmap device can't draw LCD text.
@@ -146,28 +134,21 @@
 public:
     virtual ~SkGlyphRunPainterInterface() = default;
 
-    virtual void startRun(const SkGlyphRun& glyphRun, bool useSDFT) = 0;
-
     virtual void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                     const SkStrikeSpec& strikeSpec) = 0;
 
+    virtual void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                                    const SkStrikeSpec& strikeSpec) = 0;
+
     virtual void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                                    const SkFont& runFont,
                                     const SkStrikeSpec& strikeSpec) = 0;
 
     virtual void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                                    const SkStrikeSpec& strikeSpec,
                                    const SkFont& runFont,
                                    SkScalar minScale,
-                                   SkScalar maxScale,
-                                   bool hasWCoord) = 0;
-
-    virtual void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                                       const SkStrikeSpec& strikeSpec,
-                                       bool hasW) = 0;
-
-    virtual void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                                       const SkStrikeSpec& strikeSpec) = 0;
-
+                                   SkScalar maxScale) = 0;
 };
 
 #endif  // SkGlyphRunPainter_DEFINED
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index c863908..6245ecf 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -76,7 +76,7 @@
                                  const SkIRect& dstRect,
                                  const SkIPoint& srcOffset,
                                  sk_sp<GrTextureProxy> proxy,
-                                 GrColorType srcColorType,
+                                 SkAlphaType srcAlphaType,
                                  Direction direction,
                                  int radius,
                                  float sigma,
@@ -84,7 +84,7 @@
                                  int bounds[2]) {
     GrPaint paint;
     std::unique_ptr<GrFragmentProcessor> conv(GrGaussianConvolutionFragmentProcessor::Make(
-            std::move(proxy), srcColorType, direction, radius, sigma, mode, bounds));
+            std::move(proxy), srcAlphaType, direction, radius, sigma, mode, bounds));
     paint.addColorFragmentProcessor(std::move(conv));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     SkMatrix localMatrix = SkMatrix::MakeTrans(-SkIntToScalar(srcOffset.x()),
@@ -152,6 +152,7 @@
 static std::unique_ptr<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* context,
                                                                 sk_sp<GrTextureProxy> srcProxy,
                                                                 GrColorType srcColorType,
+                                                                SkAlphaType srcAlphaType,
                                                                 const SkIPoint& proxyOffset,
                                                                 const SkIRect& srcRect,
                                                                 const SkIPoint& srcOffset,
@@ -190,7 +191,7 @@
     if (GrTextureDomain::kIgnore_Mode == mode) {
         *contentRect = dstRect;
         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
-                             std::move(srcProxy), srcColorType, direction, radius, sigma,
+                             std::move(srcProxy), srcAlphaType, direction, radius, sigma,
                              GrTextureDomain::kIgnore_Mode, bounds);
         return dstRenderTargetContext;
     }
@@ -245,16 +246,16 @@
     if (midRect.isEmpty()) {
         // Blur radius covers srcBounds; use bounds over entire draw
         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
-                             std::move(srcProxy), srcColorType, direction, radius, sigma, mode,
+                             std::move(srcProxy), srcAlphaType, direction, radius, sigma, mode,
                              bounds);
     } else {
         // Draw right and left margins with bounds; middle without.
-        convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset,
-                             srcProxy, srcColorType, direction, radius, sigma, mode, bounds);
-        convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset,
-                             srcProxy, srcColorType, direction, radius, sigma, mode, bounds);
+        convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset, srcProxy,
+                             srcAlphaType, direction, radius, sigma, mode, bounds);
+        convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset, srcProxy,
+                             srcAlphaType, direction, radius, sigma, mode, bounds);
         convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
-                             std::move(srcProxy), srcColorType, direction, radius, sigma,
+                             std::move(srcProxy), srcAlphaType, direction, radius, sigma,
                              GrTextureDomain::kIgnore_Mode, bounds);
     }
 
@@ -267,6 +268,7 @@
 static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
                                       sk_sp<GrTextureProxy> srcProxy,
                                       GrColorType srcColorType,
+                                      SkAlphaType srcAlphaType,
                                       const SkIPoint& proxyOffset,
                                       SkIPoint* srcOffset,
                                       SkIRect* contentRect,
@@ -318,8 +320,10 @@
         }
 
         GrPaint paint;
+        auto fp = GrSimpleTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
+                                              GrSamplerState::Filter::kBilerp);
         if (GrTextureDomain::kIgnore_Mode != mode && i == 1) {
-            // GrTextureDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
+            // GrDomainEffect does not support kRepeat_Mode with GrSamplerState::Filter.
             GrTextureDomain::Mode modeForScaling = GrTextureDomain::kRepeat_Mode == mode
                                                                 ? GrTextureDomain::kDecal_Mode
                                                                 : mode;
@@ -335,21 +339,13 @@
                 domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
             }
             domain.offset(proxyOffset.x(), proxyOffset.y());
-            auto fp = GrTextureDomainEffect::Make(std::move(srcProxy),
-                                                  srcColorType,
-                                                  SkMatrix::I(),
-                                                  domain,
-                                                  modeForScaling,
-                                                  GrSamplerState::Filter::kBilerp);
-            paint.addColorFragmentProcessor(std::move(fp));
+            fp = GrDomainEffect::Make(std::move(fp), domain, modeForScaling, true);
             srcRect.offset(-(*srcOffset));
             // TODO: consume the srcOffset in both first draws and always set it to zero
             // back in GaussianBlur
             srcOffset->set(0, 0);
-        } else {
-            paint.addColorTextureProcessor(std::move(srcProxy), srcColorType, SkMatrix::I(),
-                                           GrSamplerState::ClampBilerp());
         }
+        paint.addColorFragmentProcessor(std::move(fp));
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
         dstRenderTargetContext->fillRectToRect(GrFixedClip::Disabled(), std::move(paint), GrAA::kNo,
@@ -391,6 +387,7 @@
     }
 
     GrColorType srcColorType = srcRenderTargetContext->colorInfo().colorType();
+    SkAlphaType srcAlphaType = srcRenderTargetContext->colorInfo().alphaType();
 
     srcRenderTargetContext = nullptr; // no longer needed
 
@@ -404,9 +401,9 @@
     GrPaint paint;
     SkRect domain = GrTextureDomain::MakeTexelDomain(localSrcBounds, GrTextureDomain::kClamp_Mode,
                                                      GrTextureDomain::kClamp_Mode);
-    auto fp = GrTextureDomainEffect::Make(std::move(srcProxy), srcColorType, SkMatrix::I(), domain,
-                                          GrTextureDomain::kClamp_Mode,
+    auto fp = GrSimpleTextureEffect::Make(std::move(srcProxy), srcAlphaType, SkMatrix::I(),
                                           GrSamplerState::Filter::kBilerp);
+    fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode, true);
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     GrFixedClip clip(SkIRect::MakeWH(finalW, finalH));
@@ -426,7 +423,7 @@
 std::unique_ptr<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
                                                     sk_sp<GrTextureProxy> srcProxy,
                                                     GrColorType srcColorType,
-                                                    SkAlphaType srcAT,
+                                                    SkAlphaType srcAlphaType,
                                                     const SkIPoint& proxyOffset,
                                                     sk_sp<SkColorSpace> colorSpace,
                                                     const SkIRect& dstBounds,
@@ -477,9 +474,10 @@
 
     GrTextureDomain::Mode currDomainMode = mode;
     if (scaleFactorX > 1 || scaleFactorY > 1) {
-        srcProxy = decimate(context, std::move(srcProxy), srcColorType, localProxyOffset,
-                            &srcOffset, &localSrcBounds, scaleFactorX, scaleFactorY, radiusX,
-                            radiusY, currDomainMode, finalW, finalH, colorSpace);
+        srcProxy =
+                decimate(context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset,
+                         &srcOffset, &localSrcBounds, scaleFactorX, scaleFactorY, radiusX, radiusY,
+                         currDomainMode, finalW, finalH, colorSpace);
         if (!srcProxy) {
             return nullptr;
         }
@@ -497,9 +495,9 @@
     scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
     if (sigmaX > 0.0f) {
         dstRenderTargetContext = convolve_gaussian(
-                context, std::move(srcProxy), srcColorType, localProxyOffset, srcRect, srcOffset,
-                Direction::kX, radiusX, sigmaX, &localSrcBounds, currDomainMode, finalW, finalH,
-                colorSpace, xFit);
+                context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset, srcRect,
+                srcOffset, Direction::kX, radiusX, sigmaX, &localSrcBounds, currDomainMode, finalW,
+                finalH, colorSpace, xFit);
         if (!dstRenderTargetContext) {
             return nullptr;
         }
@@ -521,9 +519,9 @@
 
     if (sigmaY > 0.0f) {
         dstRenderTargetContext = convolve_gaussian(
-                context, std::move(srcProxy), srcColorType, localProxyOffset, srcRect, srcOffset,
-                Direction::kY, radiusY, sigmaY, &localSrcBounds, currDomainMode, finalW, finalH,
-                colorSpace, yFit);
+                context, std::move(srcProxy), srcColorType, srcAlphaType, localProxyOffset, srcRect,
+                srcOffset, Direction::kY, radiusY, sigmaY, &localSrcBounds, currDomainMode, finalW,
+                finalH, colorSpace, yFit);
         if (!dstRenderTargetContext) {
             return nullptr;
         }
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
index 5db7daa..9bc1fdc 100644
--- a/src/core/SkGraphics.cpp
+++ b/src/core/SkGraphics.cpp
@@ -26,7 +26,6 @@
 #include "src/core/SkStrikeCache.h"
 #include "src/core/SkTSearch.h"
 #include "src/core/SkTypefaceCache.h"
-#include "src/utils/SkUTF.h"
 
 #include <stdlib.h>
 
diff --git a/src/core/SkImagePriv.h b/src/core/SkImagePriv.h
index 0fd4433..deeb86a 100644
--- a/src/core/SkImagePriv.h
+++ b/src/core/SkImagePriv.h
@@ -52,7 +52,7 @@
  *  SkImageInfo, or the bitmap's pixels cannot be accessed, this will return
  *  nullptr.
  */
-extern SK_API sk_sp<SkImage> SkMakeImageFromRasterBitmap(const SkBitmap&, SkCopyPixelsMode);
+extern SK_SPI sk_sp<SkImage> SkMakeImageFromRasterBitmap(const SkBitmap&, SkCopyPixelsMode);
 
 // Given an image created from SkNewImageFromBitmap, return its pixelref. This
 // may be called to see if the surface and the image share the same pixelref,
diff --git a/src/core/SkLatticeIter.h b/src/core/SkLatticeIter.h
index 16eef21..7bcfe98 100644
--- a/src/core/SkLatticeIter.h
+++ b/src/core/SkLatticeIter.h
@@ -18,7 +18,7 @@
 /**
  *  Disect a lattice request into an sequence of src-rect / dst-rect pairs
  */
-class SK_API SkLatticeIter {
+class SK_SPI SkLatticeIter {
 public:
 
     static bool Valid(int imageWidth, int imageHeight, const SkCanvas::Lattice& lattice);
diff --git a/src/core/SkMalloc.cpp b/src/core/SkMalloc.cpp
new file mode 100644
index 0000000..b32242a
--- /dev/null
+++ b/src/core/SkMalloc.cpp
@@ -0,0 +1,22 @@
+// Copyright 2019 Google LLC.
+// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
+
+#include "include/private/SkMalloc.h"
+
+#include "src/core/SkSafeMath.h"
+
+void* sk_calloc_throw(size_t count, size_t elemSize) {
+    return sk_calloc_throw(SkSafeMath::Mul(count, elemSize));
+}
+
+void* sk_malloc_throw(size_t count, size_t elemSize) {
+    return sk_malloc_throw(SkSafeMath::Mul(count, elemSize));
+}
+
+void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize) {
+    return sk_realloc_throw(buffer, SkSafeMath::Mul(count, elemSize));
+}
+
+void* sk_malloc_canfail(size_t count, size_t elemSize) {
+    return sk_malloc_canfail(SkSafeMath::Mul(count, elemSize));
+}
diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
index a9e1478..d998029 100644
--- a/src/core/SkMallocPixelRef.cpp
+++ b/src/core/SkMallocPixelRef.cpp
@@ -10,25 +10,6 @@
 #include "include/core/SkData.h"
 #include "include/core/SkImageInfo.h"
 #include "include/private/SkMalloc.h"
-#include "src/core/SkSafeMath.h"
-
-void* sk_calloc_throw(size_t count, size_t elemSize) {
-    return sk_calloc_throw(SkSafeMath::Mul(count, elemSize));
-}
-
-void* sk_malloc_throw(size_t count, size_t elemSize) {
-    return sk_malloc_throw(SkSafeMath::Mul(count, elemSize));
-}
-
-void* sk_realloc_throw(void* buffer, size_t count, size_t elemSize) {
-    return sk_realloc_throw(buffer, SkSafeMath::Mul(count, elemSize));
-}
-
-void* sk_malloc_canfail(size_t count, size_t elemSize) {
-    return sk_malloc_canfail(SkSafeMath::Mul(count, elemSize));
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
 static bool is_valid(const SkImageInfo& info) {
     if (info.width() < 0 || info.height() < 0 ||
diff --git a/src/core/SkMatrix44.cpp b/src/core/SkMatrix44.cpp
index 67e710b..3af3747 100644
--- a/src/core/SkMatrix44.cpp
+++ b/src/core/SkMatrix44.cpp
@@ -255,33 +255,35 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkMatrix44::setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
+SkMatrix44& SkMatrix44::setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
     this->setIdentity();
 
     if (!dx && !dy && !dz) {
-        return;
+        return *this;
     }
 
     fMat[3][0] = dx;
     fMat[3][1] = dy;
     fMat[3][2] = dz;
     this->setTypeMask(kTranslate_Mask);
+    return *this;
 }
 
-void SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
+SkMatrix44& SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
     if (!dx && !dy && !dz) {
-        return;
+        return *this;
     }
 
     for (int i = 0; i < 4; ++i) {
         fMat[3][i] = fMat[0][i] * dx + fMat[1][i] * dy + fMat[2][i] * dz + fMat[3][i];
     }
     this->recomputeTypeMask();
+    return *this;
 }
 
-void SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
+SkMatrix44& SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
     if (!dx && !dy && !dz) {
-        return;
+        return *this;
     }
 
     if (this->getType() & kPerspective_Mask) {
@@ -296,26 +298,28 @@
         fMat[3][2] += dz;
         this->recomputeTypeMask();
     }
+    return *this;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+SkMatrix44& SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
     this->setIdentity();
 
     if (1 == sx && 1 == sy && 1 == sz) {
-        return;
+        return *this;
     }
 
     fMat[0][0] = sx;
     fMat[1][1] = sy;
     fMat[2][2] = sz;
     this->setTypeMask(kScale_Mask);
+    return *this;
 }
 
-void SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+SkMatrix44& SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
     if (1 == sx && 1 == sy && 1 == sz) {
-        return;
+        return *this;
     }
 
     // The implementation matrix * pureScale can be shortcut
@@ -327,11 +331,12 @@
         fMat[2][i] *= sz;
     }
     this->recomputeTypeMask();
+    return *this;
 }
 
-void SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+SkMatrix44& SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
     if (1 == sx && 1 == sy && 1 == sz) {
-        return;
+        return *this;
     }
 
     for (int i = 0; i < 4; i++) {
@@ -340,6 +345,7 @@
         fMat[i][2] *= sz;
     }
     this->recomputeTypeMask();
+    return *this;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
index bad042b..9712c99 100644
--- a/src/core/SkModeColorFilter.cpp
+++ b/src/core/SkModeColorFilter.cpp
@@ -19,7 +19,6 @@
 #include "src/core/SkReadBuffer.h"
 #include "src/core/SkValidationUtils.h"
 #include "src/core/SkWriteBuffer.h"
-#include "src/utils/SkUTF.h"
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp
deleted file mode 100644
index 837aa60..0000000
--- a/src/core/SkMultiPictureDraw.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "include/core/SkCanvas.h"
-#include "include/core/SkMultiPictureDraw.h"
-#include "include/core/SkPicture.h"
-#include "src/core/SkCanvasPriv.h"
-#include "src/core/SkTaskGroup.h"
-
-void SkMultiPictureDraw::DrawData::draw() {
-    fCanvas->drawPicture(fPicture, &fMatrix, fPaint);
-}
-
-void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture,
-                                        const SkMatrix* matrix, const SkPaint* paint) {
-    fPicture = SkRef(picture);
-    fCanvas = canvas;
-    if (matrix) {
-        fMatrix = *matrix;
-    } else {
-        fMatrix.setIdentity();
-    }
-    if (paint) {
-        fPaint = new SkPaint(*paint);
-    } else {
-        fPaint = nullptr;
-    }
-}
-
-void SkMultiPictureDraw::DrawData::Reset(SkTDArray<DrawData>& data) {
-    for (int i = 0; i < data.count(); ++i) {
-        data[i].fPicture->unref();
-        delete data[i].fPaint;
-    }
-    data.rewind();
-}
-
-//////////////////////////////////////////////////////////////////////////////////////
-
-SkMultiPictureDraw::SkMultiPictureDraw(int reserve) {
-    if (reserve > 0) {
-        fGPUDrawData.setReserve(reserve);
-        fThreadSafeDrawData.setReserve(reserve);
-    }
-}
-
-void SkMultiPictureDraw::reset() {
-    DrawData::Reset(fGPUDrawData);
-    DrawData::Reset(fThreadSafeDrawData);
-}
-
-void SkMultiPictureDraw::add(SkCanvas* canvas,
-                             const SkPicture* picture,
-                             const SkMatrix* matrix,
-                             const SkPaint* paint) {
-    if (nullptr == canvas || nullptr == picture) {
-        SkDEBUGFAIL("parameters to SkMultiPictureDraw::add should be non-nullptr");
-        return;
-    }
-
-    SkTDArray<DrawData>& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData;
-    array.append()->init(canvas, picture, matrix, paint);
-}
-
-class AutoMPDReset : SkNoncopyable {
-    SkMultiPictureDraw* fMPD;
-public:
-    AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {}
-    ~AutoMPDReset() { fMPD->reset(); }
-};
-
-//#define FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING
-
-void SkMultiPictureDraw::draw(bool flush) {
-    AutoMPDReset mpdreset(this);
-
-#ifdef FORCE_SINGLE_THREAD_DRAWING_FOR_TESTING
-    for (int i = 0; i < fThreadSafeDrawData.count(); ++i) {
-        fThreadSafeDrawData[i].draw();
-    }
-#else
-    SkTaskGroup().batch(fThreadSafeDrawData.count(), [&](int i) {
-        fThreadSafeDrawData[i].draw();
-    });
-#endif
-
-    // N.B. we could get going on any GPU work from this main thread while the CPU work runs.
-    // But in practice, we've either got GPU work or CPU work, not both.
-
-    const int count = fGPUDrawData.count();
-    if (0 == count) {
-        return;
-    }
-
-    for (int i = 0; i < count; ++i) {
-        const DrawData& data = fGPUDrawData[i];
-        SkCanvas* canvas = data.fCanvas;
-        const SkPicture* picture = data.fPicture;
-
-        canvas->drawPicture(picture, &data.fMatrix, data.fPaint);
-        if (flush) {
-            canvas->flush();
-        }
-    }
-}
diff --git a/src/core/SkNormalFlatSource.h b/src/core/SkNormalFlatSource.h
index a7960b0..7fce889 100644
--- a/src/core/SkNormalFlatSource.h
+++ b/src/core/SkNormalFlatSource.h
@@ -10,7 +10,7 @@
 
 #include "src/core/SkNormalSource.h"
 
-class SK_API SkNormalFlatSourceImpl : public SkNormalSource {
+class SkNormalFlatSourceImpl : public SkNormalSource {
 public:
     SkNormalFlatSourceImpl(){}
 
diff --git a/src/core/SkNormalSource.h b/src/core/SkNormalSource.h
index 49a5257..1ef51b3 100644
--- a/src/core/SkNormalSource.h
+++ b/src/core/SkNormalSource.h
@@ -20,7 +20,7 @@
 
 /** Abstract class that generates or reads in normals for use by SkLightingShader.
 */
-class SK_API SkNormalSource : public SkFlattenable {
+class SkNormalSource : public SkFlattenable {
 public:
     virtual ~SkNormalSource() override;
 
diff --git a/src/core/SkOpts.h b/src/core/SkOpts.h
index 7481610..dd1cc9d 100644
--- a/src/core/SkOpts.h
+++ b/src/core/SkOpts.h
@@ -45,7 +45,7 @@
                            grayA_to_rgbA;   // i.e. expand to color channels and premultiply
 
     extern void (*memset16)(uint16_t[], uint16_t, int);
-    extern void SK_API (*memset32)(uint32_t[], uint32_t, int);
+    extern void SK_SPI(*memset32)(uint32_t[], uint32_t, int);
     extern void (*memset64)(uint64_t[], uint64_t, int);
 
     extern void (*rect_memset16)(uint16_t[], uint16_t, int, size_t, int);
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 0847ea5..5679a9d 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -107,8 +107,8 @@
     }
 
     ~SkAutoPathBoundsUpdate() {
-        fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
-                                        : SkPath::kUnknown_Convexity);
+        fPath->setConvexityType(fDegenerate ? SkPathConvexityType::kConvex
+                                            : SkPathConvexityType::kUnknown);
         if ((fEmpty || fHasValidBounds) && fRect.isFinite()) {
             fPath->setBounds(fRect);
         }
@@ -151,8 +151,8 @@
 void SkPath::resetFields() {
     //fPathRef is assumed to have been emptied by the caller.
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
-    fFillType = kWinding_FillType;
-    this->setConvexity(kUnknown_Convexity);
+    fFillType = SkToU8(SkPathFillType::kWinding);
+    this->setConvexityType(SkPathConvexityType::kUnknown);
     this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
 
     // We don't touch Android's fSourcePath.  It's used to track texture garbage collection, so we
@@ -187,7 +187,7 @@
     fIsVolatile      = that.fIsVolatile;
 
     // Non-atomic assignment of atomic values.
-    this->setConvexity(that.getConvexityOrUnknown());
+    this->setConvexityType(that.getConvexityTypeOrUnknown());
     this->setFirstDirection(that.getFirstDirection());
 }
 
@@ -212,9 +212,9 @@
         that.fIsVolatile = iv;
 
         // Non-atomic swaps of atomic values.
-        Convexity c = this->getConvexityOrUnknown();
-        this->setConvexity(that.getConvexityOrUnknown());
-        that.setConvexity(c);
+        SkPathConvexityType c = this->getConvexityTypeOrUnknown();
+        this->setConvexityType(that.getConvexityTypeOrUnknown());
+        that.setConvexityType(c);
 
         uint8_t fd = this->getFirstDirection();
         this->setFirstDirection(that.getFirstDirection());
@@ -271,7 +271,7 @@
 
 bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
     // This only handles non-degenerate convex paths currently.
-    if (kConvex_Convexity != this->getConvexity()) {
+    if (!this->isConvex()) {
         return false;
     }
 
@@ -455,7 +455,7 @@
     return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
 }
 
-bool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
+bool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const {
     SkDEBUGCODE(this->validate();)
     int currVerb = 0;
     const SkPoint* pts = fPathRef->points();
@@ -559,13 +559,13 @@
 }
 
 // This is the public-facing non-const setConvexity().
-void SkPath::setConvexity(Convexity c) {
-    fConvexity.store(c, std::memory_order_relaxed);
+void SkPath::setConvexityType(SkPathConvexityType c) {
+    fConvexity.store((uint8_t)c, std::memory_order_relaxed);
 }
 
 // Const hooks for working with fConvexity and fFirstDirection from const methods.
-void SkPath::setConvexity(Convexity c) const {
-    fConvexity.store(c, std::memory_order_relaxed);
+void SkPath::setConvexityType(SkPathConvexityType c) const {
+    fConvexity.store((uint8_t)c, std::memory_order_relaxed);
 }
 void SkPath::setFirstDirection(uint8_t d) const {
     fFirstDirection.store(d, std::memory_order_relaxed);
@@ -579,7 +579,7 @@
 
 #define DIRTY_AFTER_EDIT                                               \
     do {                                                               \
-        this->setConvexity(kUnknown_Convexity);                        \
+        this->setConvexityType(SkPathConvexityType::kUnknown);         \
         this->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);  \
     } while (0)
 
@@ -760,20 +760,20 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static void assert_known_direction(int dir) {
-    SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
+static void assert_known_direction(SkPathDirection dir) {
+    SkASSERT(SkPathDirection::kCW == dir || SkPathDirection::kCCW == dir);
 }
 
-SkPath& SkPath::addRect(const SkRect& rect, Direction dir) {
+SkPath& SkPath::addRect(const SkRect& rect, SkPathDirection dir) {
     return this->addRect(rect, dir, 0);
 }
 
 SkPath& SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
-                     SkScalar bottom, Direction dir) {
+                     SkScalar bottom, SkPathDirection dir) {
     return this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
 }
 
-SkPath& SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
+SkPath& SkPath::addRect(const SkRect &rect, SkPathDirection dir, unsigned startIndex) {
     assert_known_direction(dir);
     this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathPriv::FirstDirection)dir
                                                    : SkPathPriv::kUnknown_FirstDirection);
@@ -903,18 +903,18 @@
 }
 
 SkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
-                          Direction dir) {
+                          SkPathDirection dir) {
     SkRRect rrect;
     rrect.setRectRadii(rect, (const SkVector*) radii);
     return this->addRRect(rrect, dir);
 }
 
-SkPath& SkPath::addRRect(const SkRRect& rrect, Direction dir) {
+SkPath& SkPath::addRRect(const SkRRect& rrect, SkPathDirection dir) {
     // legacy start indices: 6 (CW) and 7(CCW)
-    return this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
+    return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7);
 }
 
-SkPath& SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
+SkPath& SkPath::addRRect(const SkRRect &rrect, SkPathDirection dir, unsigned startIndex) {
     assert_known_direction(dir);
 
     bool isRRect = hasOnlyMoveTos();
@@ -934,7 +934,7 @@
         SkAutoDisableDirectionCheck addc(this);
 
         // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
-        const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
+        const bool startsWithConic = ((startIndex & 1) == (dir == SkPathDirection::kCW));
         const SkScalar weight = SK_ScalarRoot2Over2;
 
         SkDEBUGCODE(int initialVerbCount = this->countVerbs());
@@ -946,7 +946,7 @@
         SkPath_RRectPointIterator rrectIter(rrect, dir, startIndex);
         // Corner iterator indices follow the collapsed radii model,
         // adjusted such that the start pt is "behind" the radii start pt.
-        const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
+        const unsigned rectStartIndex = startIndex / 2 + (dir == SkPathDirection::kCW ? 0 : 1);
         SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex);
 
         this->moveTo(rrectIter.current());
@@ -966,7 +966,7 @@
         this->close();
 
         SkPathRef::Editor ed(&fPathRef);
-        ed.setIsRRect(isRRect, dir, startIndex % 8);
+        ed.setIsRRect(isRRect, dir == SkPathDirection::kCCW, startIndex % 8);
 
         SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
     }
@@ -1006,7 +1006,7 @@
 }
 
 SkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
-                             Direction dir) {
+                             SkPathDirection dir) {
     assert_known_direction(dir);
 
     if (rx < 0 || ry < 0) {
@@ -1018,12 +1018,12 @@
     return this->addRRect(rrect, dir);
 }
 
-SkPath& SkPath::addOval(const SkRect& oval, Direction dir) {
+SkPath& SkPath::addOval(const SkRect& oval, SkPathDirection dir) {
     // legacy start index: 1
     return this->addOval(oval, dir, 1);
 }
 
-SkPath& SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
+SkPath& SkPath::addOval(const SkRect &oval, SkPathDirection dir, unsigned startPointIndex) {
     assert_known_direction(dir);
 
     /* If addOval() is called after previous moveTo(),
@@ -1048,7 +1048,7 @@
 
     SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex);
     // The corner iterator pts are tracking "behind" the oval/radii pts.
-    SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
+    SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1));
     const SkScalar weight = SK_ScalarRoot2Over2;
 
     this->moveTo(ovalIter.current());
@@ -1061,11 +1061,11 @@
 
     SkPathRef::Editor ed(&fPathRef);
 
-    ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
+    ed.setIsOval(isOval, SkPathDirection::kCCW == dir, startPointIndex % 4);
     return *this;
 }
 
-SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
+SkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
     if (r > 0) {
         this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
     }
@@ -1145,7 +1145,7 @@
 // http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
 // Note that arcSweep bool value is flipped from the original implementation.
 SkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
-                      SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
+                      SkPathDirection arcSweep, SkScalar x, SkScalar y) {
     this->injectMoveToIfNeeded();
     SkPoint srcPts[2];
     this->getLastPt(&srcPts[0]);
@@ -1196,7 +1196,7 @@
     SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
 
     SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
-    if (SkToBool(arcSweep) != SkToBool(arcLarge)) {  // flipped from the original implementation
+    if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) {  // flipped from the original implementation
         scaleFactor = -scaleFactor;
     }
     delta.scale(scaleFactor);
@@ -1208,9 +1208,9 @@
     SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
     SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
     SkScalar thetaArc = theta2 - theta1;
-    if (thetaArc < 0 && !arcSweep) {  // arcSweep flipped from the original implementation
+    if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
         thetaArc += SK_ScalarPI * 2;
-    } else if (thetaArc > 0 && arcSweep) {  // arcSweep flipped from the original implementation
+    } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) {  // arcSweep flipped from the original implementation
         thetaArc -= SK_ScalarPI * 2;
     }
 
@@ -1270,7 +1270,7 @@
 }
 
 SkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
-                       SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
+                       SkPathDirection sweep, SkScalar dx, SkScalar dy) {
     SkPoint currentPoint;
     this->getLastPt(&currentPoint);
     return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep,
@@ -1294,7 +1294,7 @@
             // Index 1 is at startAngle == 0.
             SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
             startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
-            return this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
+            return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW,
                                  (unsigned) startIndex);
         }
     }
@@ -1597,7 +1597,7 @@
         matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints());
         dst->setFirstDirection(SkPathPriv::kUnknown_FirstDirection);
     } else {
-        Convexity convexity = this->getConvexityOrUnknown();
+        SkPathConvexityType convexity = this->getConvexityTypeOrUnknown();
 
         SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
 
@@ -1617,9 +1617,9 @@
         // check, and keep convex paths marked as such after a general transform...
         //
         if (matrix.isScaleTranslate() && SkPathPriv::IsAxisAligned(*this)) {
-            dst->setConvexity(convexity);
+            dst->setConvexityType(convexity);
         } else {
-            dst->setConvexity(kUnknown_Convexity);
+            dst->setConvexityType(SkPathConvexityType::kUnknown);
         }
 
         if (this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
@@ -1871,7 +1871,7 @@
         "InverseWinding",
         "InverseEvenOdd",
     };
-    builder.printf("path.setFillType(SkPath::k%s_FillType);\n",
+    builder.printf("path.setFillType(SkPathFillType::k%s);\n",
             gFillTypeStrs[(int) this->getFillType()]);
     while ((verb = iter.next(pts)) != kDone_Verb) {
         switch (verb) {
@@ -1959,288 +1959,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifdef SK_LEGACY_PATH_CONVEXITY  // for rebaselining Chrome
-
-static int sign(SkScalar x) { return x < 0; }
-#define kValueNeverReturnedBySign   2
-
-enum DirChange {
-    kLeft_DirChange,
-    kRight_DirChange,
-    kStraight_DirChange,
-    kBackwards_DirChange,
-
-    kInvalid_DirChange
-};
-
-
-static bool almost_equal(SkScalar compA, SkScalar compB) {
-    // The error epsilon was empirically derived; worse case round rects
-    // with a mid point outset by 2x float epsilon in tests had an error
-    // of 12.
-    const int epsilon = 16;
-    if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
-        return false;
-    }
-    // no need to check for small numbers because SkPath::Iter has removed degenerate values
-    int aBits = SkFloatAs2sCompliment(compA);
-    int bBits = SkFloatAs2sCompliment(compB);
-    return aBits < bBits + epsilon && bBits < aBits + epsilon;
-}
-
-// only valid for a single contour
-struct Convexicator {
-    Convexicator()
-    : fPtCount(0)
-    , fConvexity(SkPath::kConvex_Convexity)
-    , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
-    , fIsFinite(true)
-    , fIsCurve(false)
-    , fBackwards(false) {
-        fExpectedDir = kInvalid_DirChange;
-        // warnings
-        fPriorPt.set(0,0);
-        fLastPt.set(0, 0);
-        fCurrPt.set(0, 0);
-        fLastVec.set(0, 0);
-        fFirstVec.set(0, 0);
-
-        fDx = fDy = 0;
-        fSx = fSy = kValueNeverReturnedBySign;
-    }
-
-    SkPath::Convexity getConvexity() const { return fConvexity; }
-
-    /** The direction returned is only valid if the path is determined convex */
-    SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
-
-    void addPt(const SkPoint& pt) {
-        if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
-            return;
-        }
-
-        if (0 == fPtCount) {
-            fCurrPt = pt;
-            ++fPtCount;
-        } else {
-            SkVector vec = pt - fCurrPt;
-            SkScalar lengthSqd = SkPointPriv::LengthSqd(vec);
-            if (!SkScalarIsFinite(lengthSqd)) {
-                fIsFinite = false;
-            } else if (lengthSqd) {
-                fPriorPt = fLastPt;
-                fLastPt = fCurrPt;
-                fCurrPt = pt;
-                if (++fPtCount == 2) {
-                    fFirstVec = fLastVec = vec;
-                } else {
-                    SkASSERT(fPtCount > 2);
-                    this->addVec(vec);
-                }
-
-                int sx = sign(vec.fX);
-                int sy = sign(vec.fY);
-                fDx += (sx != fSx);
-                fDy += (sy != fSy);
-                fSx = sx;
-                fSy = sy;
-
-                if (fDx > 3 || fDy > 3) {
-                    fConvexity = SkPath::kConcave_Convexity;
-                }
-            }
-        }
-    }
-
-    void close() {
-        if (fPtCount > 2) {
-            this->addVec(fFirstVec);
-        }
-    }
-
-    DirChange directionChange(const SkVector& curVec) {
-        SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
-
-        SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
-        SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
-        largest = SkTMax(largest, -smallest);
-
-        if (!almost_equal(largest, largest + cross)) {
-            int sign = SkScalarSignAsInt(cross);
-            if (sign) {
-                return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
-            }
-        }
-
-        if (cross) {
-            double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
-            double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
-            double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
-            double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
-            double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
-            if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
-                int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
-                if (sign) {
-                    return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
-                }
-            }
-        }
-
-        if (!SkScalarNearlyZero(SkPointPriv::LengthSqd(fLastVec),
-                                SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
-            !SkScalarNearlyZero(SkPointPriv::LengthSqd(curVec),
-                                SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
-            fLastVec.dot(curVec) < 0.0f) {
-            return kBackwards_DirChange;
-        }
-
-        return kStraight_DirChange;
-    }
-
-    bool hasBackwards() const {
-        return fBackwards;
-    }
-
-    bool isFinite() const {
-        return fIsFinite;
-    }
-
-    void setCurve(bool isCurve) {
-        fIsCurve = isCurve;
-    }
-
-private:
-    void addVec(const SkVector& vec) {
-        SkASSERT(vec.fX || vec.fY);
-        DirChange dir = this->directionChange(vec);
-        switch (dir) {
-            case kLeft_DirChange:       // fall through
-            case kRight_DirChange:
-                if (kInvalid_DirChange == fExpectedDir) {
-                    fExpectedDir = dir;
-                    fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
-                                                                : SkPathPriv::kCCW_FirstDirection;
-                } else if (dir != fExpectedDir) {
-                    fConvexity = SkPath::kConcave_Convexity;
-                    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
-                }
-                fLastVec = vec;
-                break;
-            case kStraight_DirChange:
-                break;
-            case kBackwards_DirChange:
-                if (fIsCurve) {
-                    // If any of the subsequent dir is non-backward, it'll be concave.
-                    // Otherwise, it's still convex.
-                    fExpectedDir = dir;
-                }
-                fLastVec = vec;
-                fBackwards = true;
-                break;
-            case kInvalid_DirChange:
-                SK_ABORT("Use of invalid direction change flag");
-                break;
-        }
-    }
-
-    SkPoint             fPriorPt;
-    SkPoint             fLastPt;
-    SkPoint             fCurrPt;
-    // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
-    // value with the current vec is deemed to be of a significant value.
-    SkVector            fLastVec, fFirstVec;
-    int                 fPtCount;   // non-degenerate points
-    DirChange           fExpectedDir;
-    SkPath::Convexity   fConvexity;
-    SkPathPriv::FirstDirection   fFirstDirection;
-    int                 fDx, fDy, fSx, fSy;
-    bool                fIsFinite;
-    bool                fIsCurve;
-    bool                fBackwards;
-};
-
-SkPath::Convexity SkPath::internalGetConvexity() const {
-    // Sometimes we think we need to calculate convexity but another thread already did.
-    auto c = this->getConvexityOrUnknown();
-    if (c != kUnknown_Convexity) {
-        return c;
-    }
-
-    SkPoint         pts[4];
-    SkPath::Verb    verb;
-    SkPath::Iter    iter(*this, true);
-
-    int             contourCount = 0;
-    int             count;
-    Convexicator    state;
-
-    if (!isFinite()) {
-        return kUnknown_Convexity;
-    }
-    while ((verb = iter.next(pts, false, false)) != SkPath::kDone_Verb) {
-        switch (verb) {
-            case kMove_Verb:
-                if (++contourCount > 1) {
-                    this->setConvexity(kConcave_Convexity);
-                    return kConcave_Convexity;
-                }
-                pts[1] = pts[0];
-                // fall through
-            case kLine_Verb:
-                count = 1;
-                state.setCurve(false);
-                break;
-            case kQuad_Verb:
-                // fall through
-            case kConic_Verb:
-                // fall through
-            case kCubic_Verb:
-                count = 2 + (kCubic_Verb == verb);
-                // As an additional enhancement, this could set curve true only
-                // if the curve is nonlinear
-                state.setCurve(true);
-                break;
-            case kClose_Verb:
-                state.setCurve(false);
-                state.close();
-                count = 0;
-                break;
-            default:
-                SkDEBUGFAIL("bad verb");
-                this->setConvexity(kConcave_Convexity);
-                return kConcave_Convexity;
-        }
-
-        for (int i = 1; i <= count; i++) {
-            state.addPt(pts[i]);
-        }
-        // early exit
-        if (!state.isFinite()) {
-            return kUnknown_Convexity;
-        }
-        if (kConcave_Convexity == state.getConvexity()) {
-            this->setConvexity(kConcave_Convexity);
-            return kConcave_Convexity;
-        }
-    }
-    this->setConvexity(state.getConvexity());
-
-    if (this->getConvexityOrUnknown() == kConvex_Convexity &&
-            this->getFirstDirection() == SkPathPriv::kUnknown_FirstDirection) {
-
-        if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
-                && !this->getBounds().isEmpty()
-                && !state.hasBackwards()) {
-            this->setConvexity(Convexity::kConcave_Convexity);
-        } else {
-            this->setFirstDirection(state.getFirstDirection());
-        }
-    }
-    return this->getConvexityOrUnknown();
-}
-
-#else
-
 static int sign(SkScalar x) { return x < 0; }
 #define kValueNeverReturnedBySign   2
 
@@ -2294,7 +2012,7 @@
         return true;
     }
 
-    static SkPath::Convexity BySign(const SkPoint points[], int count) {
+    static SkPathConvexityType BySign(const SkPoint points[], int count) {
         const SkPoint* last = points + count;
         SkPoint currPt = *points++;
         SkPoint firstPt = currPt;
@@ -2308,14 +2026,14 @@
                 if (!vec.isZero()) {
                     // give up if vector construction failed
                     if (!vec.isFinite()) {
-                        return SkPath::kUnknown_Convexity;
+                        return SkPathConvexityType::kUnknown;
                     }
                     int sx = sign(vec.fX);
                     int sy = sign(vec.fY);
                     dxes += (sx != lastSx);
                     dyes += (sy != lastSy);
                     if (dxes > 3 || dyes > 3) {
-                        return SkPath::kConcave_Convexity;
+                        return SkPathConvexityType::kConcave;
                     }
                     lastSx = sx;
                     lastSy = sy;
@@ -2327,7 +2045,7 @@
             }
             points = &firstPt;
         }
-        return SkPath::kConvex_Convexity;  // that is, it may be convex, don't know yet
+        return SkPathConvexityType::kConvex;  // that is, it may be convex, don't know yet
     }
 
     bool close() {
@@ -2407,13 +2125,13 @@
     bool                fIsFinite { true };
 };
 
-SkPath::Convexity SkPath::internalGetConvexity() const {
+SkPathConvexityType SkPath::internalGetConvexity() const {
     SkPoint         pts[4];
     SkPath::Verb    verb;
     SkPath::Iter    iter(*this, true);
-    auto setComputedConvexity = [=](Convexity convexity){
-        SkASSERT(kUnknown_Convexity != convexity);
-        this->setConvexity(convexity);
+    auto setComputedConvexity = [=](SkPathConvexityType convexity){
+        SkASSERT(SkPathConvexityType::kUnknown != convexity);
+        this->setConvexityType(convexity);
         return convexity;
     };
 
@@ -2431,15 +2149,15 @@
             ++points;
         }
         --points;
-        SkPath::Convexity convexity = Convexicator::BySign(points, (int) (last - points));
-        if (SkPath::kConcave_Convexity == convexity) {
-            return setComputedConvexity(SkPath::kConcave_Convexity);
-        } else if (SkPath::kUnknown_Convexity == convexity) {
-            return SkPath::kUnknown_Convexity;
+        SkPathConvexityType convexity = Convexicator::BySign(points, (int) (last - points));
+        if (SkPathConvexityType::kConcave == convexity) {
+            return setComputedConvexity(SkPathConvexityType::kConcave);
+        } else if (SkPathConvexityType::kUnknown == convexity) {
+            return SkPathConvexityType::kUnknown;
         }
         iter.setPath(*this, true);
     } else if (!this->isFinite()) {
-        return kUnknown_Convexity;
+        return SkPathConvexityType::kUnknown;
     }
 
     int             contourCount = 0;
@@ -2447,16 +2165,16 @@
     Convexicator    state;
     auto setFail = [=](){
         if (!state.isFinite()) {
-            return SkPath::kUnknown_Convexity;
+            return SkPathConvexityType::kUnknown;
         }
-        return setComputedConvexity(SkPath::kConcave_Convexity);
+        return setComputedConvexity(SkPathConvexityType::kConcave);
     };
 
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         switch (verb) {
             case kMove_Verb:
                 if (++contourCount > 1) {
-                    return setComputedConvexity(kConcave_Convexity);
+                    return setComputedConvexity(SkPathConvexityType::kConcave);
                 }
                 state.setMovePt(pts[0]);
                 count = 0;
@@ -2480,7 +2198,7 @@
                 break;
             default:
                 SkDEBUGFAIL("bad verb");
-                return setComputedConvexity(kConcave_Convexity);
+                return setComputedConvexity(SkPathConvexityType::kConcave);
         }
         for (int i = 1; i <= count; i++) {
             if (!state.addPt(pts[i])) {
@@ -2493,16 +2211,16 @@
         if (state.getFirstDirection() == SkPathPriv::kUnknown_FirstDirection
                 && !this->getBounds().isEmpty()) {
             return setComputedConvexity(state.reversals() < 3 ?
-                    kConvex_Convexity : kConcave_Convexity);
+                    SkPathConvexityType::kConvex : SkPathConvexityType::kConcave);
         }
         this->setFirstDirection(state.getFirstDirection());
     }
-    return setComputedConvexity(kConvex_Convexity);
+    return setComputedConvexity(SkPathConvexityType::kConvex);
 }
 
 bool SkPathPriv::IsConvex(const SkPoint points[], int count) {
-    SkPath::Convexity convexity = Convexicator::BySign(points, count);
-    if (SkPath::kConvex_Convexity != convexity) {
+    SkPathConvexityType convexity = Convexicator::BySign(points, count);
+    if (SkPathConvexityType::kConvex != convexity) {
         return false;
     }
     Convexicator state;
@@ -2522,8 +2240,6 @@
             || state.reversals() < 3;
 }
 
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 class ContourIter {
@@ -2701,7 +2417,7 @@
 
     // We don't want to pay the cost for computing convexity if it is unknown,
     // so we call getConvexityOrUnknown() instead of isConvex().
-    if (path.getConvexityOrUnknown() == SkPath::kConvex_Convexity) {
+    if (path.getConvexityTypeOrUnknown() == SkPathConvexityType::kConvex) {
         SkASSERT(path.getFirstDirection() == kUnknown_FirstDirection);
         *dir = static_cast<FirstDirection>(path.getFirstDirection());
         return false;
@@ -3224,8 +2940,8 @@
                 break;
        }
     } while (!done);
-    bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
-            || SkPath::kInverseEvenOdd_FillType == this->getFillType();
+    bool evenOddFill = SkPathFillType::kEvenOdd        == this->getNewFillType()
+                    || SkPathFillType::kInverseEvenOdd == this->getNewFillType();
     if (evenOddFill) {
         w &= 1;
     }
@@ -3294,7 +3010,7 @@
     return conic.chopIntoQuadsPOW2(pts, pow2);
 }
 
-bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
+bool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPathDirection* direction,
                                     unsigned* start) {
     if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
         return false;
@@ -3364,22 +3080,22 @@
     switch (sortFlags) {
         case 0b00:
             rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
-            *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+            *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
             *start = 0;
             break;
         case 0b01:
             rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
-            *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+            *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
             *start = 1;
             break;
         case 0b10:
             rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
-            *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+            *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
             *start = 3;
             break;
         case 0b11:
             rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
-            *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+            *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
             *start = 2;
             break;
     }
@@ -3407,7 +3123,7 @@
 
     path->reset();
     path->setIsVolatile(true);
-    path->setFillType(SkPath::kWinding_FillType);
+    path->setFillType(SkPathFillType::kWinding);
     if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
         path->addOval(oval);
         SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
@@ -3441,7 +3157,7 @@
     if (useCenter) {
         path->close();
     }
-    path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
+    path->setConvexityType(convex ? SkPathConvexityType::kConvex : SkPathConvexityType::kConcave);
     path->setFirstDirection(firstDir);
 }
 
@@ -3560,7 +3276,7 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
 bool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb,
-                               const SkPoint** ptsPtr, bool* isClosed, SkPath::Direction* direction,
+                               const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
                                SkRect* rect) {
     int corners = 0;
     SkPoint closeXY;  // used to determine if final line falls on a diagonal
@@ -3693,17 +3409,17 @@
     }
     if (direction) {
         *direction = directions[0] == ((directions[1] + 1) & 3) ?
-                     SkPath::kCW_Direction : SkPath::kCCW_Direction;
+                     SkPathDirection::kCW : SkPathDirection::kCCW;
     }
     return true;
 }
 
 
-bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPath::Direction dirs[2]) {
+bool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) {
     SkDEBUGCODE(path.validate();)
     int currVerb = 0;
     const SkPoint* pts = path.fPathRef->points();
-    SkPath::Direction testDirs[2];
+    SkPathDirection testDirs[2];
     SkRect testRects[2];
     if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) {
         return false;
diff --git a/src/core/SkPathMakers.h b/src/core/SkPathMakers.h
index a4ef7e1..8e668e5 100644
--- a/src/core/SkPathMakers.h
+++ b/src/core/SkPathMakers.h
@@ -8,15 +8,15 @@
 #ifndef SkPathMakers_DEFINED
 #define SkPathMakers_DEFINED
 
-#include "include/core/SkPath.h"    // just for direction
+#include "include/core/SkPathTypes.h"
 #include "include/core/SkPoint.h"
 #include "include/core/SkRRect.h"
 
 template <unsigned N> class SkPath_PointIterator {
 public:
-    SkPath_PointIterator(SkPath::Direction dir, unsigned startIndex)
+    SkPath_PointIterator(SkPathDirection dir, unsigned startIndex)
     : fCurrent(startIndex % N)
-    , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
+    , fAdvance(dir == SkPathDirection::kCW ? 1 : N - 1) { }
 
     const SkPoint& current() const {
         SkASSERT(fCurrent < N);
@@ -38,7 +38,7 @@
 
 class SkPath_RectPointIterator : public SkPath_PointIterator<4> {
 public:
-    SkPath_RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
+    SkPath_RectPointIterator(const SkRect& rect, SkPathDirection dir, unsigned startIndex)
         : SkPath_PointIterator(dir, startIndex) {
 
         fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
@@ -50,7 +50,7 @@
 
 class SkPath_OvalPointIterator : public SkPath_PointIterator<4> {
 public:
-    SkPath_OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
+    SkPath_OvalPointIterator(const SkRect& oval, SkPathDirection dir, unsigned startIndex)
         : SkPath_PointIterator(dir, startIndex) {
 
         const SkScalar cx = oval.centerX();
@@ -65,7 +65,7 @@
 
 class SkPath_RRectPointIterator : public SkPath_PointIterator<8> {
 public:
-    SkPath_RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
+    SkPath_RRectPointIterator(const SkRRect& rrect, SkPathDirection dir, unsigned startIndex)
         : SkPath_PointIterator(dir, startIndex) {
 
         const SkRect& bounds = rrect.getBounds();
diff --git a/src/core/SkPathPriv.h b/src/core/SkPathPriv.h
index 7fc5966..803d8f48 100644
--- a/src/core/SkPathPriv.h
+++ b/src/core/SkPathPriv.h
@@ -10,6 +10,11 @@
 
 #include "include/core/SkPath.h"
 
+static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
+static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
+static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch");
+static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch");
+
 class SkPathPriv {
 public:
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
@@ -19,12 +24,12 @@
 #endif
 
     enum FirstDirection : int {
-        kCW_FirstDirection,         // == SkPath::kCW_Direction
-        kCCW_FirstDirection,        // == SkPath::kCCW_Direction
+        kCW_FirstDirection,         // == SkPathDirection::kCW
+        kCCW_FirstDirection,        // == SkPathDirection::kCCW
         kUnknown_FirstDirection,
     };
 
-    static FirstDirection AsFirstDirection(SkPath::Direction dir) {
+    static FirstDirection AsFirstDirection(SkPathDirection dir) {
         // since we agree numerically for the values in Direction, we can just cast.
         return (FirstDirection)dir;
     }
@@ -96,7 +101,7 @@
      * followed by four lines or a move followed by 3 lines and a close. None of the parameters are
      * optional. This does not permit degenerate line or point rectangles.
      */
-    static bool IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
+    static bool IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPathDirection* direction,
                                    unsigned* start);
 
     /**
@@ -158,7 +163,6 @@
         return path.fPathRef->conicWeights();
     }
 
-#ifndef SK_LEGACY_PATH_CONVEXITY
     /** Returns true if path formed by pts is convex.
 
         @param pts    SkPoint array of path
@@ -167,7 +171,6 @@
         @return       true if pts represent a convex geometry
     */
     static bool IsConvex(const SkPoint pts[], int count);
-#endif
 
     /** Returns true if the underlying SkPathRef has one single owner. */
     static bool TestingOnly_unique(const SkPath& path) {
@@ -179,7 +182,7 @@
      return true though SkPath draws oval.
 
      rect receives bounds of oval.
-     dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if
+     dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
      counterclockwise.
      start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left.
 
@@ -188,15 +191,15 @@
      Triggers performance optimizations on some GPU surface implementations.
 
      @param rect   storage for bounding SkRect of oval; may be nullptr
-     @param dir    storage for SkPath::Direction; may be nullptr
+     @param dir    storage for SkPathDirection; may be nullptr
      @param start  storage for start of oval; may be nullptr
      @return       true if SkPath was constructed by method that reduces to oval
      */
-    static bool IsOval(const SkPath& path, SkRect* rect, SkPath::Direction* dir, unsigned* start) {
+    static bool IsOval(const SkPath& path, SkRect* rect, SkPathDirection* dir, unsigned* start) {
         bool isCCW = false;
         bool result = path.fPathRef->isOval(rect, &isCCW, start);
         if (dir && result) {
-            *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+            *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
         }
         return result;
     }
@@ -206,7 +209,7 @@
      will not return true though SkPath draws SkRRect.
 
      rrect receives bounds of SkRRect.
-     dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if
+     dir receives SkPathDirection of oval: kCW_Direction if clockwise, kCCW_Direction if
      counterclockwise.
      start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left.
 
@@ -215,16 +218,16 @@
      Triggers performance optimizations on some GPU surface implementations.
 
      @param rrect  storage for bounding SkRect of SkRRect; may be nullptr
-     @param dir    storage for SkPath::Direction; may be nullptr
+     @param dir    storage for SkPathDirection; may be nullptr
      @param start  storage for start of SkRRect; may be nullptr
      @return       true if SkPath contains only SkRRect
      */
-    static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPath::Direction* dir,
+    static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPathDirection* dir,
                         unsigned* start) {
         bool isCCW = false;
         bool result = path.fPathRef->isRRect(rrect, &isCCW, start);
         if (dir && result) {
-            *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+            *dir = isCCW ? SkPathDirection::kCCW : SkPathDirection::kCW;
         }
         return result;
     }
@@ -282,22 +285,37 @@
     }
 
     static bool IsRectContour(const SkPath&, bool allowPartial, int* currVerb,
-                              const SkPoint** ptsPtr, bool* isClosed, SkPath::Direction* direction,
+                              const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction,
                               SkRect* rect);
 
     /** Returns true if SkPath is equivalent to nested SkRect pair when filled.
      If false, rect and dirs are unchanged.
      If true, rect and dirs are written to if not nullptr:
      setting rect[0] to outer SkRect, and rect[1] to inner SkRect;
-     setting dirs[0] to SkPath::Direction of outer SkRect, and dirs[1] to SkPath::Direction of
+     setting dirs[0] to SkPathDirection of outer SkRect, and dirs[1] to SkPathDirection of
      inner SkRect.
 
      @param rect  storage for SkRect pair; may be nullptr
-     @param dirs  storage for SkPath::Direction pair; may be nullptr
+     @param dirs  storage for SkPathDirection pair; may be nullptr
      @return      true if SkPath contains nested SkRect pair
      */
     static bool IsNestedFillRects(const SkPath&, SkRect rect[2],
-                                  SkPath::Direction dirs[2] = nullptr);
+                                  SkPathDirection dirs[2] = nullptr);
+
+    static bool IsInverseFillType(SkPathFillType fill) {
+        return (static_cast<int>(fill) & 2) != 0;
+    }
+
+    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
+     .
+
+     @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
+     kInverseWinding_FillType, kInverseEvenOdd_FillType
+     @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
+     */
+    static SkPathFillType ConvertToNonInverseFillType(SkPathFillType fill) {
+        return (SkPathFillType)(static_cast<int>(fill) & 1);
+    }
 };
 
 // Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics).
@@ -313,6 +331,7 @@
     const SkScalar* fConicWeights;
     SkPoint         fScratch[2];    // for auto-close lines
     bool            fNeedsCloseLine;
+    bool            fNextIsNewContour;
     SkDEBUGCODE(bool fIsConic);
 
     enum {
@@ -341,6 +360,7 @@
     struct Result {
         const SkPoint*  fPts;   // points for the segment, or null if done
         Edge            fEdge;
+        bool            fIsNewContour;
 
         // Returns true when it holds an Edge, false when the path is done.
         operator bool() { return fPts != nullptr; }
@@ -351,7 +371,8 @@
             fScratch[0] = fPts[-1];
             fScratch[1] = *fMoveToPtr;
             fNeedsCloseLine = false;
-            return Result{ fScratch, Edge::kLine };
+            fNextIsNewContour = true;
+            return Result{ fScratch, Edge::kLine, false };
         };
 
         for (;;) {
@@ -359,7 +380,7 @@
             if (fVerbs == fVerbsStop) {
                 return fNeedsCloseLine
                     ? closeline()
-                    : Result{ nullptr, Edge(kIllegalEdgeValue) };
+                    : Result{ nullptr, Edge(kIllegalEdgeValue), false };
             }
 
             SkDEBUGCODE(fIsConic = false;)
@@ -390,7 +411,9 @@
                     SkDEBUGCODE(fIsConic = (v == SkPath::kConic_Verb);)
                     SkASSERT(fIsConic == (cws_count > 0));
 
-                    return { &fPts[-(pts_count + 1)], Edge(v) };
+                    bool isNewContour = fNextIsNewContour;
+                    fNextIsNewContour = false;
+                    return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
                 }
             }
         }
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp
index a3d9e08..75086a0 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -687,5 +687,6 @@
     }
 
     fNeedsCloseLine = false;
+    fNextIsNewContour = false;
     SkDEBUGCODE(fIsConic = false;)
 }
diff --git a/src/core/SkPath_serial.cpp b/src/core/SkPath_serial.cpp
index 44fd146..b1d85f4 100644
--- a/src/core/SkPath_serial.cpp
+++ b/src/core/SkPath_serial.cpp
@@ -44,8 +44,8 @@
     return packed & kVersion_SerializationMask;
 }
 
-static SkPath::FillType extract_filltype(uint32_t packed) {
-    return static_cast<SkPath::FillType>((packed >> kFillType_SerializationShift) & 0x3);
+static SkPathFillType extract_filltype(uint32_t packed) {
+    return static_cast<SkPathFillType>((packed >> kFillType_SerializationShift) & 0x3);
 }
 
 static SerializationType extract_serializationtype(uint32_t packed) {
@@ -167,17 +167,17 @@
     SkASSERT(extract_serializationtype(packed) == SerializationType::kRRect);
 
     uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
-    FillType fillType = extract_filltype(packed);
+    SkPathFillType fillType = extract_filltype(packed);
 
-    Direction rrectDir;
+    SkPathDirection rrectDir;
     SkRRect rrect;
     int32_t start;
     switch (dir) {
         case SkPathPriv::kCW_FirstDirection:
-            rrectDir = kCW_Direction;
+            rrectDir = SkPathDirection::kCW;
             break;
         case SkPathPriv::kCCW_FirstDirection:
-            rrectDir = kCCW_Direction;
+            rrectDir = SkPathDirection::kCCW;
             break;
         default:
             return 0;
diff --git a/src/core/SkRasterPipeline.cpp b/src/core/SkRasterPipeline.cpp
index 4f73643..78b943a 100644
--- a/src/core/SkRasterPipeline.cpp
+++ b/src/core/SkRasterPipeline.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "include/private/SkImageInfoPriv.h"
 #include "src/core/SkColorSpacePriv.h"
 #include "src/core/SkOpts.h"
 #include "src/core/SkRasterPipeline.h"
@@ -291,12 +292,11 @@
     }
 }
 
-void SkRasterPipeline::append_gamut_clamp_if_normalized(const SkImageInfo& dstInfo) {
-    // N.B. we _do_ clamp for kRGBA_F16Norm_SkColorType... because it's normalized.
-    if (dstInfo.colorType() != kRGBA_F16_SkColorType &&
-        dstInfo.colorType() != kRGBA_F32_SkColorType &&
-        dstInfo.alphaType() == kPremul_SkAlphaType)
-    {
+// Clamp premul values to [0,alpha] (logical [0,1]) to avoid the confusing
+// scenario of being able to store a logical color channel > 1.0 when alpha < 1.0.
+// Most software that works with normalized premul values expect r,g,b channels all <= a.
+void SkRasterPipeline::append_gamut_clamp_if_normalized(const SkImageInfo& info) {
+    if (info.alphaType() == kPremul_SkAlphaType && SkColorTypeIsNormalized(info.colorType())) {
         this->unchecked_append(SkRasterPipeline::clamp_gamut, nullptr);
     }
 }
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index efa50bc..7ef7ae4 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -12,7 +12,6 @@
 #include "include/private/SkTo.h"
 #include "src/core/SkRegionPriv.h"
 #include "src/core/SkSafeMath.h"
-#include "src/utils/SkUTF.h"
 
 #include <utility>
 
diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp
index 50ce42b..b312eed 100644
--- a/src/core/SkRemoteGlyphCache.cpp
+++ b/src/core/SkRemoteGlyphCache.cpp
@@ -7,21 +7,25 @@
 
 #include "src/core/SkRemoteGlyphCache.h"
 
+#include <bitset>
 #include <iterator>
 #include <memory>
 #include <new>
 #include <string>
 #include <tuple>
 
+#include "include/private/SkChecksum.h"
 #include "src/core/SkDevice.h"
 #include "src/core/SkDraw.h"
 #include "src/core/SkEnumerate.h"
 #include "src/core/SkGlyphRun.h"
+#include "src/core/SkSpan.h"
 #include "src/core/SkStrike.h"
 #include "src/core/SkStrikeCache.h"
 #include "src/core/SkTLazy.h"
 #include "src/core/SkTraceEvent.h"
 #include "src/core/SkTypeface_remote.h"
+#include "src/core/SkZip.h"
 
 #if SK_SUPPORT_GPU
 #include "src/gpu/GrDrawOpAtlas.h"
@@ -190,8 +194,38 @@
     /* n X (glyphs ids) */
 };
 
+// Represent a set of x sub-pixel-position glyphs with a glyph id < kMaxGlyphID and
+// y sub-pxiel-position must be 0. Most sub-pixel-positioned glyphs have been x-axis aligned
+// forcing the y sub-pixel position to be zero. We can organize the SkPackedGlyphID to check that
+// the glyph id and the y position == 0 with a single compare in the following way:
+//    <y-sub-pixel-position>:2 | <glyphid:16> | <x-sub-pixel-position>:2
+// This organization allows a single check of a packed-id to be:
+//    packed-id < kMaxGlyphID * possible-x-sub-pixel-positions
+// where possible-x-sub-pixel-positions == 4.
+class LowerRangeBitVector {
+public:
+    bool test(SkPackedGlyphID packedID) const {
+        uint32_t bit = packedID.value();
+        return bit < kMaxIndex && fBits.test(bit);
+    }
+    void setIfLower(SkPackedGlyphID packedID) {
+        uint32_t bit = packedID.value();
+        if (bit < kMaxIndex) {
+            fBits.set(bit);
+        }
+    }
+
+private:
+    using GID = SkPackedGlyphID;
+    static_assert(GID::kSubPixelX < GID::kGlyphID && GID::kGlyphID < GID::kSubPixelY,
+            "SkPackedGlyphID must be organized: sub-y | glyph id | sub-x");
+    static constexpr int kMaxGlyphID = 128;
+    static constexpr int kMaxIndex = kMaxGlyphID * (1u << GID::kSubPixelPosLen);
+    std::bitset<kMaxIndex> fBits;
+};
+
 // -- RemoteStrike ----------------------------------------------------------------------------
-class SkStrikeServer::RemoteStrike : public SkStrikeForGPU {
+class SkStrikeServer::RemoteStrike final : public SkStrikeForGPU {
 public:
     // N.B. RemoteStrike is not valid until ensureScalerContext is called.
     RemoteStrike(const SkDescriptor& descriptor,
@@ -199,7 +233,6 @@
                  SkDiscardableHandleId discardableHandleId);
     ~RemoteStrike() override;
 
-    void addGlyph(SkPackedGlyphID, bool asPath);
     void writePendingGlyphs(Serializer* serializer);
     SkDiscardableHandleId discardableHandleId() const { return fDiscardableHandleId; }
 
@@ -213,32 +246,74 @@
         return fRoundingSpec;
     }
 
-    void prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawables) override;
+    void prepareForMaskDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
+
+    void prepareForSDFTDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
+
+    void prepareForPathDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
 
     void onAboutToExitScope() override {}
 
     bool hasPendingGlyphs() const {
-        return !fPendingGlyphImages.empty() || !fPendingGlyphPaths.empty();
+        return !fMasksToSend.empty() || !fPathsToSend.empty();
     }
 
     void resetScalerContext();
 
 private:
-    void writeGlyphPath(const SkPackedGlyphID& glyphID, Serializer* serializer) const;
+    template <typename Rejector>
+    void commonMaskLoop(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects, Rejector&& reject);
 
+    // Keep track of if the glyph draw has been totally satisfied. It could be that this
+    // strike can not draw the glyph, and it must be rejected to be handled by fallback.
+    // For example, if a glyph has canDrawAsMask sent, then that data is on the GPU, and this
+    // strike totally satisfies this result. If canDrawAsMask is false, then this glyph must be
+    // rejected, and handled by a later stage using a latter strike.
+    struct MaskSummary {
+        static_assert(SkPackedGlyphID::kMaskAll < (1u << 30), "SkPackedGlyphID is too big.");
+        uint32_t packedID:30;
+        uint32_t canDrawAsMask:1;
+        uint32_t canDrawAsSDFT:1;
+    };
+
+    struct MaskSummaryTraits {
+        static SkPackedGlyphID GetKey(MaskSummary summary) {
+            return SkPackedGlyphID{summary.packedID};
+        }
+
+        static uint32_t Hash(SkPackedGlyphID packedID) {
+            return packedID.hash();
+        }
+    };
+
+    // Same thing as MaskSummary, but for paths.
+    struct PathSummary {
+        constexpr static uint16_t kIsPath = 0;
+        SkGlyphID glyphID;
+        // If drawing glyphID can be done with a path, this is 0, otherwise it is the max
+        // dimension of the glyph.
+        uint16_t maxDimensionOrPath;
+    };
+
+    struct PathSummaryTraits {
+        static SkGlyphID GetKey(PathSummary summary) {
+            return summary.glyphID;
+        }
+
+        static uint32_t Hash(SkGlyphID packedID) {
+            return SkChecksum::CheapMix(packedID);
+        }
+    };
+
+    void writeGlyphPath(const SkGlyph& glyph, Serializer* serializer) const;
     void ensureScalerContext();
 
-    // The set of glyphs cached on the remote client.
-    SkTHashSet<SkPackedGlyphID> fCachedGlyphImages;
-    SkTHashSet<SkPackedGlyphID> fCachedGlyphPaths;
-
-    // The set of glyphs which has not yet been serialized and sent to the
-    // remote client.
-    std::vector<SkPackedGlyphID> fPendingGlyphImages;
-    std::vector<SkPackedGlyphID> fPendingGlyphPaths;
-
+    const int fNumberOfGlyphs;
     const SkAutoDescriptor fDescriptor;
-
     const SkDiscardableHandleId fDiscardableHandleId;
 
     const SkGlyphPositionRoundingSpec fRoundingSpec;
@@ -251,58 +326,41 @@
     const SkTypeface* fTypeface{nullptr};
     SkScalerContextEffects fEffects;
 
+    // Have the metrics been sent for this strike. Only send them once.
     bool fHaveSentFontMetrics{false};
 
-    class GlyphMapHashTraits {
-    public:
-        static SkPackedGlyphID GetKey(const SkGlyph* glyph) {
-            return glyph->getPackedID();
-        }
-        static uint32_t Hash(SkPackedGlyphID glyphId) {
-            return glyphId.hash();
-        }
-    };
+    LowerRangeBitVector fSentLowGlyphIDs;
 
-    // FallbackTextHelper cases require glyph metrics when analyzing a glyph run, in which case
-    // we cache them here.
-    SkTHashTable<SkGlyph*, SkPackedGlyphID, GlyphMapHashTraits> fGlyphMap;
+    // The masks and paths that currently reside in the GPU process.
+    SkTHashTable<MaskSummary, SkPackedGlyphID, MaskSummaryTraits> fSentGlyphs;
+    SkTHashTable<PathSummary, SkGlyphID, PathSummaryTraits> fSentPaths;
 
-    SkArenaAlloc fAlloc{256};
+    // The Masks, SDFT Mask, and Paths that need to be sent to the GPU task for the processed
+    // TextBlobs. Cleared after diffs are serialized.
+    std::vector<SkGlyph> fMasksToSend;
+    std::vector<SkGlyph> fPathsToSend;
+
+    // Alloc for storing bits and pieces of paths, Cleared after diffs are serialized.
+    SkArenaAlloc fPathAlloc{256};
 };
 
 SkStrikeServer::RemoteStrike::RemoteStrike(
         const SkDescriptor& descriptor,
         std::unique_ptr<SkScalerContext> context,
         uint32_t discardableHandleId)
-        : fDescriptor{descriptor}
+        : fNumberOfGlyphs(context->getGlyphCount())
+        , fDescriptor{descriptor}
         , fDiscardableHandleId(discardableHandleId)
         , fRoundingSpec{context->isSubpixel(), context->computeAxisAlignmentForHText()}
         // N.B. context must come last because it is used above.
-        , fContext{std::move(context)} {
+        , fContext{std::move(context)}
+        , fSentLowGlyphIDs{} {
     SkASSERT(fDescriptor.getDesc() != nullptr);
     SkASSERT(fContext != nullptr);
 }
 
 SkStrikeServer::RemoteStrike::~RemoteStrike() = default;
 
-void SkStrikeServer::RemoteStrike::addGlyph(SkPackedGlyphID glyph, bool asPath) {
-    auto* cache = asPath ? &fCachedGlyphPaths : &fCachedGlyphImages;
-    auto* pending = asPath ? &fPendingGlyphPaths : &fPendingGlyphImages;
-
-    // Already cached.
-    if (cache->contains(glyph)) {
-        return;
-    }
-
-    // A glyph is going to be sent. Make sure we have a scaler context to send it.
-    this->ensureScalerContext();
-
-    // Serialize and cache. Also create the scalar context to use when serializing
-    // this glyph.
-    cache->add(glyph);
-    pending->push_back(glyph);
-}
-
 size_t SkStrikeServer::MapOps::operator()(const SkDescriptor* key) const {
     return key->getChecksum();
 }
@@ -311,7 +369,6 @@
     return *lhs == *rhs;
 }
 
-
 // -- TrackLayerDevice -----------------------------------------------------------------------------
 class SkTextBlobCacheDiffCanvas::TrackLayerDevice final : public SkNoPixelsDevice {
 public:
@@ -449,7 +506,7 @@
 
 void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) {
     size_t strikesToSend = 0;
-    fRemoteStrikesToSend.foreach ([&strikesToSend](RemoteStrike* strike) {
+    fRemoteStrikesToSend.foreach ([&](RemoteStrike* strike) {
         if (strike->hasPendingGlyphs()) {
             strikesToSend++;
         } else {
@@ -472,7 +529,7 @@
     serializer.emplace<uint64_t>(SkTo<uint64_t>(strikesToSend));
     fRemoteStrikesToSend.foreach (
 #ifdef SK_DEBUG
-            [&serializer, this](RemoteStrike* strike) {
+            [&](RemoteStrike* strike) {
                 if (strike->hasPendingGlyphs()) {
                     strike->writePendingGlyphs(&serializer);
                     strike->resetScalerContext();
@@ -514,8 +571,10 @@
 }
 
 void SkStrikeServer::AddGlyphForTesting(
-        RemoteStrike* cache, SkPackedGlyphID glyphID, bool asPath) {
-    cache->addGlyph(glyphID, asPath);
+        RemoteStrike* strike, SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    strike->prepareForMaskDrawing(drawables, rejects);
+    rejects->flipRejectsToSource();
+    SkASSERT(rejects->source().empty());
 }
 
 void SkStrikeServer::checkForDeletedEntries() {
@@ -596,15 +655,15 @@
 
 // No need to write fForceBW because it is a flag private to SkScalerContext_DW, which will never
 // be called on the GPU side.
-static void writeGlyph(SkGlyph* glyph, Serializer* serializer) {
-    serializer->write<SkPackedGlyphID>(glyph->getPackedID());
-    serializer->write<float>(glyph->advanceX());
-    serializer->write<float>(glyph->advanceY());
-    serializer->write<uint16_t>(glyph->width());
-    serializer->write<uint16_t>(glyph->height());
-    serializer->write<int16_t>(glyph->top());
-    serializer->write<int16_t>(glyph->left());
-    serializer->write<uint8_t>(glyph->maskFormat());
+static void writeGlyph(const SkGlyph& glyph, Serializer* serializer) {
+    serializer->write<SkPackedGlyphID>(glyph.getPackedID());
+    serializer->write<float>(glyph.advanceX());
+    serializer->write<float>(glyph.advanceY());
+    serializer->write<uint16_t>(glyph.width());
+    serializer->write<uint16_t>(glyph.height());
+    serializer->write<int16_t>(glyph.top());
+    serializer->write<int16_t>(glyph.left());
+    serializer->write<uint8_t>(glyph.maskFormat());
 }
 
 void SkStrikeServer::RemoteStrike::writePendingGlyphs(Serializer* serializer) {
@@ -623,35 +682,30 @@
         fHaveSentFontMetrics = true;
     }
 
-    // Write glyphs images.
-    serializer->emplace<uint64_t>(fPendingGlyphImages.size());
-    for (const auto& glyphID : fPendingGlyphImages) {
-        SkGlyph glyph{glyphID};
-        fContext->getMetrics(&glyph);
+    // Write mask glyphs
+    serializer->emplace<uint64_t>(fMasksToSend.size());
+    for (SkGlyph& glyph : fMasksToSend) {
         SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat));
 
-        writeGlyph(&glyph, serializer);
+        writeGlyph(glyph, serializer);
         auto imageSize = glyph.imageSize();
-        if (imageSize == 0u) continue;
-
-        glyph.fImage = serializer->allocate(imageSize, glyph.formatAlignment());
-        fContext->getImage(glyph);
-        // TODO: Generating the image can change the mask format, do we need to update it in the
-        // serialized glyph?
+        if (imageSize > 0 && FitsInAtlas(glyph)) {
+            glyph.fImage = serializer->allocate(imageSize, glyph.formatAlignment());
+            fContext->getImage(glyph);
+        }
     }
-    fPendingGlyphImages.clear();
+    fMasksToSend.clear();
 
     // Write glyphs paths.
-    serializer->emplace<uint64_t>(fPendingGlyphPaths.size());
-    for (const auto& glyphID : fPendingGlyphPaths) {
-        SkGlyph glyph{glyphID};
-        fContext->getMetrics(&glyph);
+    serializer->emplace<uint64_t>(fPathsToSend.size());
+    for (SkGlyph& glyph : fPathsToSend) {
         SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat));
 
-        writeGlyph(&glyph, serializer);
-        writeGlyphPath(glyphID, serializer);
+        writeGlyph(glyph, serializer);
+        writeGlyphPath(glyph, serializer);
     }
-    fPendingGlyphPaths.clear();
+    fPathsToSend.clear();
+    fPathAlloc.reset();
 }
 
 void SkStrikeServer::RemoteStrike::ensureScalerContext() {
@@ -671,60 +725,124 @@
     fEffects = effects;
 }
 
-void SkStrikeServer::RemoteStrike::writeGlyphPath(const SkPackedGlyphID& glyphID,
-                                                  Serializer* serializer) const {
-    SkPath path;
-    if (!fContext->getPath(glyphID, &path)) {
+void SkStrikeServer::RemoteStrike::writeGlyphPath(
+        const SkGlyph& glyph, Serializer* serializer) const {
+    if (glyph.isColor() || glyph.isEmpty()) {
         serializer->write<uint64_t>(0u);
         return;
     }
 
-    size_t pathSize = path.writeToMemory(nullptr);
+    const SkPath* path = glyph.path();
+
+    if (path == nullptr) {
+        serializer->write<uint64_t>(0u);
+        return;
+    }
+
+    size_t pathSize = path->writeToMemory(nullptr);
     serializer->write<uint64_t>(pathSize);
-    path.writeToMemory(serializer->allocate(pathSize, kPathAlignment));
+    path->writeToMemory(serializer->allocate(pathSize, kPathAlignment));
 }
 
+template <typename Rejector>
+void SkStrikeServer::RemoteStrike::commonMaskLoop(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects, Rejector&& reject) {
+    drawables->forEachGlyphID(
+            [&](size_t i, SkPackedGlyphID packedID, SkPoint position) {
+                MaskSummary* summary = fSentGlyphs.find(packedID);
+                if (summary == nullptr) {
+                    // Put the new SkGlyph in the glyphs to send.
+                    fMasksToSend.emplace_back(packedID);
+                    SkGlyph* glyph = &fMasksToSend.back();
 
-// Be sure to read and understand the comment for prepareForDrawing in
-// SkStrikeForGPU.h before working on this code.
-void SkStrikeServer::RemoteStrike::prepareForDrawing(
-        int maxDimension, SkDrawableGlyphBuffer* drawables) {
-    for (auto t : SkMakeEnumerate(drawables->input())) {
-        size_t i; SkGlyphVariant packedID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
-        SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedID);
-        // Has this glyph ever been seen before?
-        if (glyphPtr == nullptr) {
-            // Never seen before. Make a new glyph.
-            glyphPtr = fAlloc.make<SkGlyph>(packedID);
-            fGlyphMap.set(glyphPtr);
-            this->ensureScalerContext();
-            fContext->getMetrics(glyphPtr);
-            if (glyphPtr->maxDimension() <= maxDimension) {
-                // do nothing
-            } else if (!glyphPtr->isColor()) {
-                // The glyph is too big for the atlas, but it is not color, so it is handled
-                // with a path.
-                if (glyphPtr->setPath(&fAlloc, fContext.get())) {
-                    // Always send the path data, even if its not available, to make sure empty
-                    // paths are not incorrectly assumed to be cache misses.
-                    fCachedGlyphPaths.add(glyphPtr->getPackedID());
-                    fPendingGlyphPaths.push_back(glyphPtr->getPackedID());
+                    // Build the glyph
+                    this->ensureScalerContext();
+                    fContext->getMetrics(glyph);
+                    MaskSummary newSummary =
+                            {packedID.value(), CanDrawAsMask(*glyph), CanDrawAsSDFT(*glyph)};
+                    summary = fSentGlyphs.set(newSummary);
                 }
-            } else {
-                // This will be handled by the fallback strike.
-                SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
-            }
-            // Make sure to send the glyph to the GPU because we always send the image for a glyph.
-            fCachedGlyphImages.add(packedID);
-            fPendingGlyphImages.push_back(packedID);
+
+                // Reject things that are too big.
+                if (reject(*summary)) {
+                    rejects->reject(i);
+                }
+            });
+}
+
+void SkStrikeServer::RemoteStrike::prepareForMaskDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    for (auto [i, variant, _] : SkMakeEnumerate(drawables->input())) {
+        SkPackedGlyphID packedID = variant.packedID();
+        if (fSentLowGlyphIDs.test(packedID)) {
+            SkASSERT(fSentGlyphs.find(packedID) != nullptr);
+            continue;
         }
 
-        // TODO(herb): Change the code to only send the glyphs for fallback?
-        drawables->push_back(glyphPtr, i);
+        MaskSummary* summary = fSentGlyphs.find(packedID);
+        if (summary == nullptr) {
+            // Put the new SkGlyph in the glyphs to send.
+            fMasksToSend.emplace_back(packedID);
+            SkGlyph* glyph = &fMasksToSend.back();
+
+            // Build the glyph
+            this->ensureScalerContext();
+            fContext->getMetrics(glyph);
+
+            fSentLowGlyphIDs.setIfLower(packedID);
+
+            MaskSummary newSummary =
+                    {packedID.value(), CanDrawAsMask(*glyph), CanDrawAsSDFT(*glyph)};
+            summary = fSentGlyphs.set(newSummary);
+        }
+
+        // Reject things that are too big.
+        if (!summary->canDrawAsMask) {
+            rejects->reject(i);
+        }
     }
 }
 
+void SkStrikeServer::RemoteStrike::prepareForSDFTDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    this->commonMaskLoop(drawables, rejects,
+                         [](MaskSummary summary){return !summary.canDrawAsSDFT;});
+}
+
+void SkStrikeServer::RemoteStrike::prepareForPathDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    drawables->forEachGlyphID(
+        [&](size_t i, SkPackedGlyphID packedID, SkPoint position) {
+            SkGlyphID glyphID = packedID.glyphID();
+            PathSummary* summary = fSentPaths.find(glyphID);
+            if (summary == nullptr) {
+                // Put the new SkGlyph in the glyphs to send.
+                fPathsToSend.emplace_back(SkPackedGlyphID{glyphID});
+                SkGlyph* glyph = &fPathsToSend.back();
+
+                // Build the glyph
+                this->ensureScalerContext();
+                fContext->getMetrics(glyph);
+
+                uint16_t maxDimensionOrPath = glyph->maxDimension();
+                // Only try to get the path if the glyphs is not color.
+                if (!glyph->isColor() && !glyph->isEmpty()) {
+                    glyph->setPath(&fPathAlloc, fContext.get());
+                    if (glyph->path() != nullptr) {
+                        maxDimensionOrPath = PathSummary::kIsPath;
+                    }
+                }
+
+                PathSummary newSummary = {glyph->getGlyphID(), maxDimensionOrPath};
+                summary = fSentPaths.set(newSummary);
+            }
+
+            if (summary->maxDimensionOrPath != PathSummary::kIsPath) {
+                rejects->reject(i, (int)summary->maxDimensionOrPath);
+            }
+        });
+}
+
 // SkStrikeClient ----------------------------------------------------------------------------------
 class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner {
 public:
@@ -781,13 +899,12 @@
     SkASSERT(memorySize != 0u);
     Deserializer deserializer(static_cast<const volatile char*>(memory), memorySize);
 
-    uint64_t typefaceSize = 0u;
-    uint64_t strikeCount = 0u;
-    uint64_t glyphImagesCount = 0u;
-    uint64_t glyphPathsCount = 0u;
+    uint64_t typefaceSize = 0;
+    uint64_t strikeCount = 0;
+    uint64_t glyphImagesCount = 0;
+    uint64_t glyphPathsCount = 0;
 
     if (!deserializer.read<uint64_t>(&typefaceSize)) READ_FAILURE
-
     for (size_t i = 0; i < typefaceSize; ++i) {
         WireTypeface wire;
         if (!deserializer.read<WireTypeface>(&wire)) READ_FAILURE
@@ -850,7 +967,7 @@
             SkTLazy<SkGlyph> glyph;
             if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE
 
-            if (!glyph->isEmpty()) {
+            if (!glyph->isEmpty() && SkStrikeForGPU::FitsInAtlas(*glyph)) {
                 const volatile void* image =
                         deserializer.read(glyph->imageSize(), glyph->formatAlignment());
                 if (!image) READ_FAILURE
diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h
index ffc69bf..540f792 100644
--- a/src/core/SkRemoteGlyphCache.h
+++ b/src/core/SkRemoteGlyphCache.h
@@ -54,11 +54,11 @@
     SkTextBlobCacheDiffCanvas(int width, int height, const SkSurfaceProps& props,
                               SkStrikeServer* strikeServer, bool DFTSupport = true);
 
-    SK_API SkTextBlobCacheDiffCanvas(int width, int height, const SkSurfaceProps& props,
+    SK_SPI SkTextBlobCacheDiffCanvas(int width, int height, const SkSurfaceProps& props,
                                      SkStrikeServer* strikeServer, sk_sp<SkColorSpace> colorSpace,
                                      bool DFTSupport);
 
-    SK_API ~SkTextBlobCacheDiffCanvas() override;
+    SK_SPI ~SkTextBlobCacheDiffCanvas() override;
 
 protected:
     SkCanvas::SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override;
@@ -79,11 +79,11 @@
     // entries on the remote client.
     class DiscardableHandleManager {
     public:
-        SK_API virtual ~DiscardableHandleManager() = default;
+        SK_SPI virtual ~DiscardableHandleManager() = default;
 
         // Creates a new *locked* handle and returns a unique ID that can be used to identify
         // it on the remote client.
-        SK_API virtual SkDiscardableHandleId createHandle() = 0;
+        SK_SPI virtual SkDiscardableHandleId createHandle() = 0;
 
         // Returns true if the handle could be successfully locked. The server can
         // assume it will remain locked until the next set of serialized entries is
@@ -91,24 +91,24 @@
         // If returns false, the cache entry mapped to the handle has been deleted
         // on the client. Any subsequent attempts to lock the same handle are not
         // allowed.
-        SK_API virtual bool lockHandle(SkDiscardableHandleId) = 0;
+        SK_SPI virtual bool lockHandle(SkDiscardableHandleId) = 0;
 
         // Returns true if a handle has been deleted on the remote client. It is
         // invalid to use a handle id again with this manager once this returns true.
         // TODO(khushalsagar): Make pure virtual once chrome implementation lands.
-        SK_API virtual bool isHandleDeleted(SkDiscardableHandleId) { return false; }
+        SK_SPI virtual bool isHandleDeleted(SkDiscardableHandleId) { return false; }
     };
 
-    SK_API explicit SkStrikeServer(DiscardableHandleManager* discardableHandleManager);
-    SK_API ~SkStrikeServer() override;
+    SK_SPI explicit SkStrikeServer(DiscardableHandleManager* discardableHandleManager);
+    SK_SPI ~SkStrikeServer() override;
 
     // Serializes the typeface to be transmitted using this server.
-    SK_API sk_sp<SkData> serializeTypeface(SkTypeface*);
+    SK_SPI sk_sp<SkData> serializeTypeface(SkTypeface*);
 
     // Serializes the strike data captured using a SkTextBlobCacheDiffCanvas. Any
     // handles locked using the DiscardableHandleManager will be assumed to be
     // unlocked after this call.
-    SK_API void writeStrikeData(std::vector<uint8_t>* memory);
+    SK_SPI void writeStrikeData(std::vector<uint8_t>* memory);
 
     // Methods used internally in Skia ------------------------------------------
     class RemoteStrike;
@@ -125,7 +125,7 @@
                                                   const SkTypeface& typeface) override;
 
     static void AddGlyphForTesting(
-            RemoteStrike* cache, SkPackedGlyphID glyphID, bool asPath);
+            RemoteStrike* strike, SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects);
 
     void setMaxEntriesInDescriptorMapForTesting(size_t count) {
         fMaxEntriesInDescriptorMap = count;
@@ -206,20 +206,20 @@
         virtual void notifyReadFailure(const ReadFailureData& data) {}
     };
 
-    SK_API explicit SkStrikeClient(sk_sp<DiscardableHandleManager>,
+    SK_SPI explicit SkStrikeClient(sk_sp<DiscardableHandleManager>,
                                    bool isLogging = true,
                                    SkStrikeCache* strikeCache = nullptr);
-    SK_API ~SkStrikeClient();
+    SK_SPI ~SkStrikeClient();
 
     // Deserializes the typeface previously serialized using the SkStrikeServer. Returns null if the
     // data is invalid.
-    SK_API sk_sp<SkTypeface> deserializeTypeface(const void* data, size_t length);
+    SK_SPI sk_sp<SkTypeface> deserializeTypeface(const void* data, size_t length);
 
     // Deserializes the strike data from a SkStrikeServer. All messages generated
     // from a server when serializing the ops must be deserialized before the op
     // is rasterized.
     // Returns false if the data is invalid.
-    SK_API bool readStrikeData(const volatile void* memory, size_t memorySize);
+    SK_SPI bool readStrikeData(const volatile void* memory, size_t memorySize);
 
 private:
     class DiscardableStrikePinner;
diff --git a/src/core/SkScan_AAAPath.cpp b/src/core/SkScan_AAAPath.cpp
index a9c0af4..416df89 100644
--- a/src/core/SkScan_AAAPath.cpp
+++ b/src/core/SkScan_AAAPath.cpp
@@ -21,7 +21,6 @@
 #include "src/core/SkScan.h"
 #include "src/core/SkScanPriv.h"
 #include "src/core/SkTSort.h"
-#include "src/utils/SkUTF.h"
 
 #include <utility>
 
@@ -1546,7 +1545,7 @@
 
 static void aaa_walk_edges(SkAnalyticEdge*  prevHead,
                            SkAnalyticEdge*  nextTail,
-                           SkPath::FillType fillType,
+                           SkPathFillType   fillType,
                            AdditiveBlitter* blitter,
                            int              start_y,
                            int              stop_y,
@@ -1570,10 +1569,8 @@
         update_next_next_y(edge->fUpperY, y, &nextNextY);
     }
 
-    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
-    int windingMask = (fillType & 1) ? 1 : -1;
-
-    bool isInverse = SkPath::IsInverseFillType(fillType);
+    int windingMask = SkPathFillType_IsEvenOdd(fillType) ? 1 : -1;
+    bool isInverse  = SkPathFillType_IsInverse(fillType);
 
     if (isInverse && SkIntToFixed(start_y) != y) {
         int width = SkFixedFloorToInt(rightClip - leftClip);
@@ -1937,7 +1934,7 @@
 
         aaa_walk_edges(&headEdge,
                        &tailEdge,
-                       path.getFillType(),
+                       path.getNewFillType(),
                        blitter,
                        start_y,
                        stop_y,
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index 4271f4e..cacc228 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -97,14 +97,13 @@
 #define PREPOST_START   true
 #define PREPOST_END     false
 
-static void walk_edges(SkEdge* prevHead, SkPath::FillType fillType,
+static void walk_edges(SkEdge* prevHead, SkPathFillType fillType,
                        SkBlitter* blitter, int start_y, int stop_y,
                        PrePostProc proc, int rightClip) {
     validate_sort(prevHead->fNext);
 
     int curr_y = start_y;
-    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
-    int windingMask = (fillType & 1) ? 1 : -1;
+    int windingMask = SkPathFillType_IsEvenOdd(fillType) ? 1 : -1;
 
     for (;;) {
         int     w = 0;
@@ -471,8 +470,8 @@
     if (path.isConvex() && (nullptr == proc) && count >= 2) {
         walk_simple_edges(&headEdge, blitter, start_y, stop_y);
     } else {
-        walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc,
-                shiftedClip.right());
+        walk_edges(&headEdge, path.getNewFillType(), blitter, start_y, stop_y, proc,
+                   shiftedClip.right());
     }
 }
 
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index b0bface..7c3419a 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -499,9 +499,8 @@
             }
 
             sk_sp<GrTextureProxy> subsetProxy(
-                    GrSurfaceProxy::Copy(fContext, fTextureProxy.get(), fColorType,
-                                         GrMipMapped::kNo, *subset, SkBackingFit::kExact,
-                                         SkBudgeted::kYes));
+                    GrSurfaceProxy::Copy(fContext, fTextureProxy.get(), GrMipMapped::kNo, *subset,
+                                         SkBackingFit::kExact, SkBudgeted::kYes));
             if (!subsetProxy) {
                 return nullptr;
             }
diff --git a/src/core/SkSpinlock.cpp b/src/core/SkSpinlock.cpp
index 59ea2b7..c5a469b 100644
--- a/src/core/SkSpinlock.cpp
+++ b/src/core/SkSpinlock.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/private/SkSpinlock.h"
+#include "include/private/SkThreadAnnotations.h"
 
 #if 0
     #include "include/private/SkMutex.h"
@@ -41,7 +42,9 @@
     debug_trace();
 
     // To act as a mutex, we need an acquire barrier when we acquire the lock.
+    SK_POTENTIALLY_BLOCKING_REGION_BEGIN;
     while (fLocked.exchange(true, std::memory_order_acquire)) {
         do_pause();
     }
+    SK_POTENTIALLY_BLOCKING_REGION_END;
 }
diff --git a/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
index 457929d..b4fd39d 100644
--- a/src/core/SkSpriteBlitter_ARGB32.cpp
+++ b/src/core/SkSpriteBlitter_ARGB32.cpp
@@ -13,7 +13,6 @@
 #include "src/core/SkBlitRow.h"
 #include "src/core/SkSpriteBlitter.h"
 #include "src/core/SkXfermodePriv.h"
-#include "src/utils/SkUTF.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/core/SkSpriteBlitter_RGB565.cpp b/src/core/SkSpriteBlitter_RGB565.cpp
index d0d5a99..aebc05c 100644
--- a/src/core/SkSpriteBlitter_RGB565.cpp
+++ b/src/core/SkSpriteBlitter_RGB565.cpp
@@ -13,7 +13,6 @@
 #include "src/core/SkBlitRow.h"
 #include "src/core/SkSpriteBlitter.h"
 #include "src/core/SkXfermodePriv.h"
-#include "src/utils/SkUTF.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/core/SkStrike.cpp b/src/core/SkStrike.cpp
index 0241239..db05511 100644
--- a/src/core/SkStrike.cpp
+++ b/src/core/SkStrike.cpp
@@ -54,17 +54,6 @@
     return glyph;
 }
 
-SkGlyph* SkStrike::glyph(SkGlyphID glyphID) {
-    return this->glyph(SkPackedGlyphID{glyphID});
-}
-
-SkGlyph* SkStrike::glyph(SkGlyphID glyphID, SkPoint position) {
-    SkIPoint mask = fRoundingSpec.ignorePositionMask;
-    SkFixed subX = SkScalarToFixed(position.x()) & mask.x(),
-            subY = SkScalarToFixed(position.y()) & mask.y();
-    return this->glyph(SkPackedGlyphID{glyphID, subX, subY});
-}
-
 SkGlyph* SkStrike::glyphFromPrototype(const SkGlyphPrototype& p, void* image) {
     SkGlyph* glyph = fGlyphMap.findOrNull(p.id);
     if (glyph == nullptr) {
@@ -100,10 +89,6 @@
     return *fDesc.getDesc();
 }
 
-unsigned SkStrike::getGlyphCount() const {
-    return fScalerContext->getGlyphCount();
-}
-
 int SkStrike::countCachedGlyphs() const {
     return fGlyphMap.count();
 }
@@ -112,7 +97,7 @@
         SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
     const SkGlyph** cursor = results;
     for (auto glyphID : glyphIDs) {
-        SkGlyph* glyphPtr = this->glyph(glyphID);
+        SkGlyph* glyphPtr = this->glyph(SkPackedGlyphID{glyphID});
         if (pathDetail == kMetricsAndPath) {
             this->preparePath(glyphPtr);
         }
@@ -140,12 +125,7 @@
     return glyph;
 }
 
-bool SkStrike::belongsToCache(const SkGlyph* glyph) const {
-    return glyph && fGlyphMap.findOrNull(glyph->getPackedID()) == glyph;
-}
-
-const SkGlyph* SkStrike::getCachedGlyphAnySubPix(SkGlyphID glyphID,
-                                                     SkPackedGlyphID vetoID) const {
+const SkGlyph* SkStrike::getCachedGlyphAnySubPix(SkGlyphID glyphID, SkPackedGlyphID vetoID) const {
     for (SkFixed subY = 0; subY < SK_Fixed1; subY += SK_FixedQuarter) {
         for (SkFixed subX = 0; subX < SK_Fixed1; subX += SK_FixedQuarter) {
             SkPackedGlyphID packedGlyphID{glyphID, subX, subY};
@@ -181,56 +161,83 @@
     return {results, glyphIDs.size()};
 }
 
+template <typename Fn>
+void SkStrike::commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn) {
+    drawables->forEachGlyphID(
+        [&](size_t i, SkPackedGlyphID packedID, SkPoint pos) {
+            if (SkScalarsAreFinite(pos.x(), pos.y())) {
+                SkGlyph* glyph = this->glyph(packedID);
+                if (!glyph->isEmpty()) {
+                    fn(i, glyph, pos);
+                }
+            }
+        });
+}
+
 void SkStrike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
-    for (auto t : SkMakeEnumerate(drawables->input())) {
-        size_t i; SkGlyphVariant packedID;
-        std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
-        SkGlyph* glyph = this->glyph(packedID);
-        if (!glyph->isEmpty()) {
-            const void* image = this->prepareImage(glyph);
+    this->commonFilterLoop(drawables,
+        [&](size_t i, SkGlyph* glyph, SkPoint pos){
             // If the glyph is too large, then no image is created.
-            if (image != nullptr) {
+            if (this->prepareImage(glyph) != nullptr) {
                 drawables->push_back(glyph, i);
             }
-        }
-    }
+        });
 }
 
 void SkStrike::prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables) {
-    for (auto t : SkMakeEnumerate(drawables->input())) {
-        size_t i; SkGlyphVariant packedID;
-        std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
-        SkGlyph* glyph = this->glyph(packedID);
-        if (!glyph->isEmpty()) {
+    this->commonFilterLoop(drawables,
+        [&](size_t i, SkGlyph* glyph, SkPoint pos){
             const SkPath* path = this->preparePath(glyph);
             // The glyph my not have a path.
             if (path != nullptr) {
                 drawables->push_back(path, i);
             }
-        }
-    }
+        });
 }
 
-// N.B. This glyphMetrics call culls all the glyphs which will not display based on a non-finite
-// position or that there are no mask pixels.
-void SkStrike::prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawables) {
-    for (auto t : SkMakeEnumerate(drawables->input())) {
-        size_t i; SkGlyphVariant packedID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
-        if (SkScalarsAreFinite(pos.x(), pos.y())) {
-            SkGlyph* glyphPtr = this->glyph(packedID);
-            drawables->push_back(glyphPtr, i);
-            if (glyphPtr->maxDimension() <= maxDimension) {
-                // The glyph fits. Prepare image later.
-            } else if (!glyphPtr->isColor()) {
-                // The out of atlas glyph is not color so we can draw it using paths.
-                this->preparePath(glyphPtr);
+// Note: this does not actually fill out the image. That happens at atlas building time.
+void SkStrike::prepareForMaskDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    this->commonFilterLoop(drawables,
+        [&](size_t i, SkGlyph* glyph, SkPoint pos) {
+            if (CanDrawAsMask(*glyph)) {
+                drawables->push_back(glyph, i);
             } else {
-                // This will be handled by the fallback strike.
-                SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
+                rejects->reject(i);
             }
-        }
-    }
+        });
+}
+
+void SkStrike::prepareForSDFTDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    this->commonFilterLoop(drawables,
+        [&](size_t i, SkGlyph* glyph, SkPoint pos) {
+            if (CanDrawAsSDFT(*glyph)) {
+                drawables->push_back(glyph, i);
+            } else {
+                rejects->reject(i);
+            }
+        });
+}
+
+void SkStrike::prepareForPathDrawing(
+        SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
+    this->commonFilterLoop(drawables,
+        [&](size_t i, SkGlyph* glyph, SkPoint pos) {
+            if (!glyph->isColor()) {
+                const SkPath* path = this->preparePath(glyph);
+                if (path != nullptr) {
+                    // Save off the path to draw later.
+                    drawables->push_back(path, i);
+                } else {
+                    // Glyph does not have a path. It is probably bitmap only.
+                    rejects->reject(i, glyph->maxDimension());
+                }
+            } else {
+                // Glyph is color.
+                rejects->reject(i, glyph->maxDimension());
+            }
+        });
 }
 
 void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
diff --git a/src/core/SkStrike.h b/src/core/SkStrike.h
index 469f44c..0ae57ac 100644
--- a/src/core/SkStrike.h
+++ b/src/core/SkStrike.h
@@ -39,36 +39,21 @@
              std::unique_ptr<SkScalerContext> scaler,
              const SkFontMetrics&);
 
-    // Return a glyph. Create it if it doesn't exist, and initialize the glyph with metrics and
-    // advances using a scaler.
-    SkGlyph* glyph(SkPackedGlyphID packedID);
-    SkGlyph* glyph(SkGlyphID glyphID);
-    SkGlyph* glyph(SkGlyphID, SkPoint);
-
     // Return a glyph.  Create it if it doesn't exist, and initialize with the prototype.
     SkGlyph* glyphFromPrototype(const SkGlyphPrototype& p, void* image = nullptr);
 
     // Return a glyph or nullptr if it does not exits in the strike.
     SkGlyph* glyphOrNull(SkPackedGlyphID id) const;
 
-    const void* prepareImage(SkGlyph* glyph);
-
     // Lookup (or create if needed) the toGlyph using toID. If that glyph is not initialized with
     // an image, then use the information in from to initialize the width, height top, left,
     // format and image of the toGlyph. This is mainly used preserving the glyph if it was
     // created by a search of desperation.
     SkGlyph* mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from);
 
-    // If the path has never been set, then use the scaler context to add the glyph.
-    const SkPath* preparePath(SkGlyph*);
-
     // If the path has never been set, then add a path to glyph.
     const SkPath* preparePath(SkGlyph* glyph, const SkPath* path);
 
-    /** Returns the number of glyphs for this strike.
-    */
-    unsigned getGlyphCount() const;
-
     /** Return the number of glyphs currently cached. */
     int countCachedGlyphs() const;
 
@@ -78,9 +63,6 @@
     void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
                         SkGlyph* , SkScalar* array, int* count);
 
-    /** Fallback glyphs used during font remoting if the original glyph can't be found.
-     */
-    bool belongsToCache(const SkGlyph* glyph) const;
     /** Find any glyph in this cache with the given ID, regardless of subpixel positioning.
      *  If set and present, skip over the glyph with vetoID.
      */
@@ -93,10 +75,6 @@
         return fFontMetrics;
     }
 
-    SkMask::Format getMaskFormat() const {
-        return fScalerContext->getMaskFormat();
-    }
-
     const SkGlyphPositionRoundingSpec& roundingSpec() const override {
         return fRoundingSpec;
     }
@@ -115,8 +93,15 @@
     void prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables);
 
     void prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables);
-    void  prepareForDrawing(
-            int maxDimension, SkDrawableGlyphBuffer* drawables)override;
+
+    void prepareForMaskDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
+
+    void prepareForSDFTDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
+
+    void prepareForPathDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) override;
 
     void onAboutToExitScope() override;
 
@@ -166,11 +151,22 @@
 
     SkGlyph* makeGlyph(SkPackedGlyphID);
 
+    template <typename Fn>
+    void commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn);
+
+    // Return a glyph. Create it if it doesn't exist, and initialize the glyph with metrics and
+    // advances using a scaler.
+    SkGlyph* glyph(SkPackedGlyphID packedID);
+
+    const void* prepareImage(SkGlyph* glyph);
+
+    // If the path has never been set, then use the scaler context to add the glyph.
+    const SkPath* preparePath(SkGlyph*);
+
     enum PathDetail {
         kMetricsOnly,
         kMetricsAndPath
     };
-
     // internalPrepare will only be called with a mutex already held.
     SkSpan<const SkGlyph*> internalPrepare(
             SkSpan<const SkGlyphID> glyphIDs,
@@ -179,7 +175,7 @@
 
     const SkAutoDescriptor                 fDesc;
     const std::unique_ptr<SkScalerContext> fScalerContext;
-    SkFontMetrics                          fFontMetrics;
+    const SkFontMetrics                    fFontMetrics;
 
     // Map from a combined GlyphID and sub-pixel position to a SkGlyph*.
     // The actual glyph is stored in the fAlloc. This structure provides an
diff --git a/src/core/SkStrikeCache.cpp b/src/core/SkStrikeCache.cpp
index d5b0591..e8faad0 100644
--- a/src/core/SkStrikeCache.cpp
+++ b/src/core/SkStrikeCache.cpp
@@ -32,14 +32,25 @@
         return fStrike.roundingSpec();
     }
 
-    void prepareForDrawing(int maxDimension, SkDrawableGlyphBuffer* drawbles) override {
-        fStrike.prepareForDrawing(maxDimension, drawbles);
-    }
-
     const SkDescriptor& getDescriptor() const override {
         return fStrike.getDescriptor();
     }
 
+    void prepareForMaskDrawing(
+            SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
+        fStrike.prepareForMaskDrawing(drawbles, rejects);
+    }
+
+    void prepareForSDFTDrawing(
+            SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
+        fStrike.prepareForSDFTDrawing(drawbles, rejects);
+    }
+
+    void prepareForPathDrawing(
+            SkDrawableGlyphBuffer* drawbles, SkSourceGlyphBuffer* rejects) override {
+        fStrike.prepareForPathDrawing(drawbles, rejects);
+    }
+
     void onAboutToExitScope() override {
         fStrikeCache->attachNode(this);
     }
diff --git a/src/core/SkStrikeForGPU.cpp b/src/core/SkStrikeForGPU.cpp
index 1c2da44..9f1f32c 100644
--- a/src/core/SkStrikeForGPU.cpp
+++ b/src/core/SkStrikeForGPU.cpp
@@ -12,12 +12,11 @@
 #include "src/core/SkGlyphRunPainter.h"
 
 bool SkStrikeForGPU::CanDrawAsMask(const SkGlyph& glyph) {
-    return glyph.maxDimension() <= SkStrikeCommon::kSkSideTooBigForAtlas;
+    return FitsInAtlas(glyph);
 }
 
 bool SkStrikeForGPU::CanDrawAsSDFT(const SkGlyph& glyph) {
-    return glyph.maxDimension() <= SkStrikeCommon::kSkSideTooBigForAtlas
-           && glyph.maskFormat() == SkMask::kSDF_Format;
+    return FitsInAtlas(glyph) && glyph.maskFormat() == SkMask::kSDF_Format;
 }
 
 bool SkStrikeForGPU::CanDrawAsPath(const SkGlyph& glyph) {
@@ -25,3 +24,7 @@
     return !glyph.isColor() && glyph.path() != nullptr;
 }
 
+bool SkStrikeForGPU::FitsInAtlas(const SkGlyph& glyph) {
+    return glyph.maxDimension() <= SkStrikeCommon::kSkSideTooBigForAtlas;
+}
+
diff --git a/src/core/SkStrikeForGPU.h b/src/core/SkStrikeForGPU.h
index 783cf94..9c7b4a5 100644
--- a/src/core/SkStrikeForGPU.h
+++ b/src/core/SkStrikeForGPU.h
@@ -21,6 +21,7 @@
 class SkGlyph;
 class SkMaskFilter;
 class SkPathEffect;
+class SkSourceGlyphBuffer;
 class SkTypeface;
 struct SkGlyphPositionRoundingSpec;
 struct SkScalerContextEffects;
@@ -30,14 +31,14 @@
     virtual ~SkStrikeForGPU() = default;
     virtual const SkDescriptor& getDescriptor() const = 0;
 
-    // prepareForDrawing takes glyphIDs, and position in the form of
-    // SkDrawableGlyphBuffer, and returns a list of SkGlyphs and positions where all the data to
-    // draw the glyph has been created by adjusting the SkDrawableGlyphBuffer. The maxDimension
-    // parameter determines if the mask/SDF version will be created, or an alternate drawing
-    // format should be used. For path-only drawing set maxDimension to 0, and for bitmap-device
-    // drawing (where there is no upper limit to the glyph in the cache) use INT_MAX.
-    virtual void
-    prepareForDrawing(int maxGlyphDimension, SkDrawableGlyphBuffer* drawables) = 0;
+    virtual void prepareForMaskDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) = 0;
+
+    virtual void prepareForSDFTDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) = 0;
+
+    virtual void prepareForPathDrawing(
+            SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) = 0;
 
     virtual const SkGlyphPositionRoundingSpec& roundingSpec() const = 0;
 
@@ -48,6 +49,7 @@
     static bool CanDrawAsMask(const SkGlyph& glyph);
     static bool CanDrawAsSDFT(const SkGlyph& glyph);
     static bool CanDrawAsPath(const SkGlyph& glyph);
+    static bool FitsInAtlas(const SkGlyph& glyph);
 
 
     struct Deleter {
diff --git a/src/core/SkStrikeSpec.cpp b/src/core/SkStrikeSpec.cpp
index 505aefc..5b10bae 100644
--- a/src/core/SkStrikeSpec.cpp
+++ b/src/core/SkStrikeSpec.cpp
@@ -13,6 +13,11 @@
 #include "src/core/SkStrikeCache.h"
 #include "src/core/SkTLazy.h"
 
+#if SK_SUPPORT_GPU
+#include "src/gpu/text/GrStrikeCache.h"
+#include "src/gpu/text/GrTextContext.h"
+#endif
+
 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
                                     const SkSurfaceProps& surfaceProps,
                                     SkScalerContextFlags scalerContextFlags,
@@ -239,18 +244,48 @@
     return fStrike->metrics(glyphIDs, fGlyphs.get());
 }
 
+const SkGlyph* SkBulkGlyphMetrics::glyph(SkGlyphID glyphID) {
+    return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
+}
+
 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
     : fStrike{spec.findOrCreateExclusiveStrike()} { }
 
+SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(SkExclusiveStrikePtr&& strike)
+        : fStrike{std::move(strike)} { }
+
 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
     fGlyphs.reset(glyphIDs.size());
     return fStrike->preparePaths(glyphIDs, fGlyphs.get());
 }
 
+const SkGlyph* SkBulkGlyphMetricsAndPaths::glyph(SkGlyphID glyphID) {
+    return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
+}
+
+void SkBulkGlyphMetricsAndPaths::findIntercepts(
+    const SkScalar* bounds, SkScalar scale, SkScalar xPos,
+    const SkGlyph* glyph, SkScalar* array, int* count) {
+    // TODO(herb): remove this abominable const_cast. Do the intercepts really need to be on the
+    //  glyph?
+    fStrike->findIntercepts(bounds, scale, xPos, const_cast<SkGlyph*>(glyph), array, count);
+}
+
 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
         : fStrike{spec.findOrCreateExclusiveStrike()} { }
 
+SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(SkExclusiveStrikePtr&& strike)
+        : fStrike{std::move(strike)} { }
+
 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
     fGlyphs.reset(glyphIDs.size());
     return fStrike->prepareImages(glyphIDs, fGlyphs.get());
 }
+
+const SkGlyph* SkBulkGlyphMetricsAndImages::glyph(SkPackedGlyphID packedID) {
+    return this->glyphs(SkSpan<const SkPackedGlyphID>{&packedID, 1})[0];
+}
+
+const SkDescriptor& SkBulkGlyphMetricsAndImages::descriptor() const {
+    return fStrike->getDescriptor();
+}
diff --git a/src/core/SkStrikeSpec.h b/src/core/SkStrikeSpec.h
index d34e711..f40032a 100644
--- a/src/core/SkStrikeSpec.h
+++ b/src/core/SkStrikeSpec.h
@@ -13,8 +13,9 @@
 #include "src/core/SkStrikeForGPU.h"
 
 #if SK_SUPPORT_GPU
-#include "src/gpu/text/GrStrikeCache.h"
 #include "src/gpu/text/GrTextContext.h"
+class GrStrikeCache;
+class GrTextStrike;
 #endif
 
 class SkFont;
@@ -40,10 +41,10 @@
             SkScalerContextFlags scalerContextFlags);
 
     static SkStrikeSpec MakeSourceFallback(const SkFont& font,
-                                                  const SkPaint& paint,
-                                                  const SkSurfaceProps& surfaceProps,
-                                                  SkScalerContextFlags scalerContextFlags,
-                                                  SkScalar maxSourceGlyphDimension);
+                                           const SkPaint& paint,
+                                           const SkSurfaceProps& surfaceProps,
+                                           SkScalerContextFlags scalerContextFlags,
+                                           SkScalar maxSourceGlyphDimension);
 
     // Create a canonical strike spec for device-less measurements.
     static SkStrikeSpec MakeCanonicalized(
@@ -99,6 +100,7 @@
 public:
     explicit SkBulkGlyphMetrics(const SkStrikeSpec& spec);
     SkSpan<const SkGlyph*> glyphs(SkSpan<const SkGlyphID> glyphIDs);
+    const SkGlyph* glyph(SkGlyphID glyphID);
 
 private:
     static constexpr int kTypicalGlyphCount = 20;
@@ -109,7 +111,11 @@
 class SkBulkGlyphMetricsAndPaths {
 public:
     explicit SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec);
+    explicit SkBulkGlyphMetricsAndPaths(SkExclusiveStrikePtr&& strike);
     SkSpan<const SkGlyph*> glyphs(SkSpan<const SkGlyphID> glyphIDs);
+    const SkGlyph* glyph(SkGlyphID glyphID);
+    void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
+                        const SkGlyph* glyph, SkScalar* array, int* count);
 
 private:
     static constexpr int kTypicalGlyphCount = 20;
@@ -120,7 +126,11 @@
 class SkBulkGlyphMetricsAndImages {
 public:
     explicit SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec);
-    SkSpan<const SkGlyph*> glyphs(SkSpan<const SkPackedGlyphID> glyphIDs);
+    explicit SkBulkGlyphMetricsAndImages(SkExclusiveStrikePtr&& strike);
+    SkSpan<const SkGlyph*> glyphs(SkSpan<const SkPackedGlyphID> packedIDs);
+    const SkGlyph* glyph(SkPackedGlyphID packedID);
+    const SkDescriptor& descriptor() const;
+
 
 private:
     static constexpr int kTypicalGlyphCount = 64;
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index ecfefdf..1c32900 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -1390,8 +1390,8 @@
     // If src is really a rect, call our specialty strokeRect() method
     {
         SkRect rect;
-        bool isClosed;
-        SkPath::Direction dir;
+        bool isClosed = false;
+        SkPathDirection dir;
         if (src.isRect(&rect, &isClosed, &dir) && isClosed) {
             this->strokeRect(rect, dst, dir);
             // our answer should preserve the inverseness of the src
@@ -1498,15 +1498,15 @@
     }
 }
 
-static SkPath::Direction reverse_direction(SkPath::Direction dir) {
-    static const SkPath::Direction gOpposite[] = { SkPath::kCCW_Direction, SkPath::kCW_Direction };
-    return gOpposite[dir];
+static SkPathDirection reverse_direction(SkPathDirection dir) {
+    static const SkPathDirection gOpposite[] = { SkPathDirection::kCCW, SkPathDirection::kCW };
+    return gOpposite[(int)dir];
 }
 
-static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
+static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPathDirection dir) {
     SkPoint pts[8];
 
-    if (SkPath::kCW_Direction == dir) {
+    if (SkPathDirection::kCW == dir) {
         pts[0].set(r.fLeft, outer.fTop);
         pts[1].set(r.fRight, outer.fTop);
         pts[2].set(outer.fRight, r.fTop);
@@ -1529,7 +1529,7 @@
 }
 
 void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
-                          SkPath::Direction dir) const {
+                          SkPathDirection dir) const {
     SkASSERT(dst != nullptr);
     dst->reset();
 
diff --git a/src/core/SkStroke.h b/src/core/SkStroke.h
index 66edf3d..b0458d3 100644
--- a/src/core/SkStroke.h
+++ b/src/core/SkStroke.h
@@ -62,7 +62,7 @@
      *  Stroke the specified rect, winding it in the specified direction..
      */
     void    strokeRect(const SkRect& rect, SkPath* result,
-                       SkPath::Direction = SkPath::kCW_Direction) const;
+                       SkPathDirection = SkPathDirection::kCW) const;
     void    strokePath(const SkPath& path, SkPath*) const;
 
     ////////////////////////////////////////////////////////////////
diff --git a/src/core/SkTextBlob.cpp b/src/core/SkTextBlob.cpp
index 3c84bda..7044b50 100644
--- a/src/core/SkTextBlob.cpp
+++ b/src/core/SkTextBlob.cpp
@@ -875,27 +875,27 @@
     interceptPaint.setPathEffect(nullptr);
 
     SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(interceptFont, &interceptPaint);
-    auto cache = strikeSpec.findOrCreateExclusiveStrike();
+    SkBulkGlyphMetricsAndPaths metricsAndPaths{strikeSpec};
 
     SkScalar xOffset = 0;
     SkScalar xPos = xOffset;
     SkScalar prevAdvance = 0;
 
     const SkPoint* posCursor = glyphRun.positions().begin();
-    for (auto glyphID : glyphRun.glyphsIDs()) {
+    for (const SkGlyph* glyph : metricsAndPaths.glyphs(glyphRun.glyphsIDs())) {
         SkPoint pos = *posCursor++;
 
-        SkGlyph* glyph = cache->glyph(glyphID);
         xPos += prevAdvance * scale;
         prevAdvance = glyph->advanceX();
-        if (cache->preparePath(glyph) != nullptr) {
+        if (glyph->path() != nullptr) {
             // The typeface is scaled, so un-scale the bounds to be in the space of the typeface.
             // Also ensure the bounds are properly offset by the vertical positioning of the glyph.
             SkScalar scaledBounds[2] = {
                 (bounds[0] - pos.y()) / scale,
                 (bounds[1] - pos.y()) / scale
             };
-            cache->findIntercepts(scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
+            metricsAndPaths.findIntercepts(
+                    scaledBounds, scale, pos.x(), glyph, intervals, intervalCount);
         }
     }
     return *intervalCount;
diff --git a/src/core/SkTypeface_remote.cpp b/src/core/SkTypeface_remote.cpp
index 838a783..dd6c2e2 100644
--- a/src/core/SkTypeface_remote.cpp
+++ b/src/core/SkTypeface_remote.cpp
@@ -45,7 +45,7 @@
 
     // Since the scaler context is being called, we don't have the needed data. Try to find a
     // fallback before failing.
-    if (fCache && fCache->belongsToCache(glyph)) {
+    if (fCache && fCache->glyphOrNull(glyph->getPackedID()) != nullptr) {
         // First check the original cache, in case there is a sub-pixel pos mismatch.
         if (const SkGlyph* from =
                     fCache->getCachedGlyphAnySubPix(glyph->getGlyphID(), glyph->getPackedID())) {
diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp
index 6fdd0a6..1152a63 100644
--- a/src/core/SkVM.cpp
+++ b/src/core/SkVM.cpp
@@ -6,19 +6,62 @@
  */
 
 #include "include/core/SkStream.h"
+#include "include/core/SkString.h"
+#include "include/private/SkChecksum.h"
 #include "include/private/SkSpinlock.h"
 #include "include/private/SkTFitsIn.h"
 #include "include/private/SkThreadID.h"
 #include "include/private/SkVx.h"
 #include "src/core/SkCpu.h"
 #include "src/core/SkVM.h"
+#include <functional>  // std::hash
 #include <string.h>
+
+// JIT code isn't MSAN-instrumented, so we won't see when it uses
+// uninitialized memory, and we'll not see the writes it makes as properly
+// initializing memory.  Instead force the interpreter, which should let
+// MSAN see everything our programs do properly.
+#if defined(__has_feature)
+    #if __has_feature(memory_sanitizer)
+        #undef SKVM_JIT
+    #endif
+#endif
+
 #if defined(SKVM_JIT)
     #include <sys/mman.h>
-#endif
-#if defined(SKVM_PERF_DUMPS)
-    #include <stdio.h>
-    #include <time.h>
+
+    #if defined(SKVM_PERF_DUMPS)
+        #include <stdio.h>
+        #include <time.h>
+    #endif
+
+    #if defined(SKVM_JIT_VTUNE)
+        #include <jitprofiling.h>
+        static void notify_vtune(const char* name, void* addr, size_t len,
+                                 const std::vector<skvm::LineTableEntry>& lines) {
+            if (iJIT_IsProfilingActive() != iJIT_SAMPLING_ON) {
+                return;
+            }
+
+            std::vector<LineNumberInfo> table;
+            for (auto& entry : lines) {
+                table.push_back({ (unsigned)entry.offset, (unsigned)entry.line });
+            }
+
+            iJIT_Method_Load event;
+            memset(&event, 0, sizeof(event));
+            event.method_id           = iJIT_GetNewMethodID();
+            event.method_name         = const_cast<char*>(name);  // wtf?
+            event.method_load_address = addr;
+            event.method_size         = len;
+            event.line_number_table   = table.data();
+            event.line_number_size    = table.size();
+            iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &event);
+        }
+    #else
+        static void notify_vtune(const char* name, void* addr, size_t len,
+                                 const std::vector<skvm::LineTableEntry>& lines) {}
+    #endif
 #endif
 
 
@@ -95,40 +138,47 @@
             Val  x = inst.x,
                  y = inst.y,
                  z = inst.z;
-            int imm = inst.imm;
+            int immy = inst.immy,
+                immz = inst.immz;
             write(o, !inst.can_hoist    ? "  " :
                       inst.used_in_loop ? "↑ " :
                                           "↟ ");
             switch (op) {
-                case Op::store8:  write(o, "store8" , Arg{imm}, V{x}); break;
-                case Op::store16: write(o, "store16", Arg{imm}, V{x}); break;
-                case Op::store32: write(o, "store32", Arg{imm}, V{x}); break;
+                case Op::assert_true:  write(o, "assert_true" , V{x}); break;
 
-                case Op::load8:  write(o, V{id}, "= load8" , Arg{imm}); break;
-                case Op::load16: write(o, V{id}, "= load16", Arg{imm}); break;
-                case Op::load32: write(o, V{id}, "= load32", Arg{imm}); break;
+                case Op::store8:  write(o, "store8" , Arg{immy}, V{x}); break;
+                case Op::store16: write(o, "store16", Arg{immy}, V{x}); break;
+                case Op::store32: write(o, "store32", Arg{immy}, V{x}); break;
 
-                case Op::gather8:  write(o, V{id}, "= gather8" , Arg{imm}, V{x}); break;
-                case Op::gather16: write(o, V{id}, "= gather16", Arg{imm}, V{x}); break;
-                case Op::gather32: write(o, V{id}, "= gather32", Arg{imm}, V{x}); break;
+                case Op::index: write(o, V{id}, "= index"); break;
 
-                case Op::uniform8:  write(o, V{id}, "= uniform8" , Arg{imm & 0xffff}, Hex{imm>>16}); break;
-                case Op::uniform16: write(o, V{id}, "= uniform16", Arg{imm & 0xffff}, Hex{imm>>16}); break;
-                case Op::uniform32: write(o, V{id}, "= uniform32", Arg{imm & 0xffff}, Hex{imm>>16}); break;
+                case Op::load8:  write(o, V{id}, "= load8" , Arg{immy}); break;
+                case Op::load16: write(o, V{id}, "= load16", Arg{immy}); break;
+                case Op::load32: write(o, V{id}, "= load32", Arg{immy}); break;
 
-                case Op::splat:  write(o, V{id}, "= splat", Splat{imm}); break;
+                case Op::gather8:  write(o, V{id}, "= gather8" , Arg{immy}, V{x}); break;
+                case Op::gather16: write(o, V{id}, "= gather16", Arg{immy}, V{x}); break;
+                case Op::gather32: write(o, V{id}, "= gather32", Arg{immy}, V{x}); break;
+
+                case Op::uniform8:  write(o, V{id}, "= uniform8" , Arg{immy}, Hex{immz}); break;
+                case Op::uniform16: write(o, V{id}, "= uniform16", Arg{immy}, Hex{immz}); break;
+                case Op::uniform32: write(o, V{id}, "= uniform32", Arg{immy}, Hex{immz}); break;
+
+                case Op::splat:  write(o, V{id}, "= splat", Splat{immy}); break;
 
 
                 case Op::add_f32: write(o, V{id}, "= add_f32", V{x}, V{y}      ); break;
                 case Op::sub_f32: write(o, V{id}, "= sub_f32", V{x}, V{y}      ); break;
                 case Op::mul_f32: write(o, V{id}, "= mul_f32", V{x}, V{y}      ); break;
                 case Op::div_f32: write(o, V{id}, "= div_f32", V{x}, V{y}      ); break;
+                case Op::min_f32: write(o, V{id}, "= min_f32", V{x}, V{y}      ); break;
+                case Op::max_f32: write(o, V{id}, "= max_f32", V{x}, V{y}      ); break;
                 case Op::mad_f32: write(o, V{id}, "= mad_f32", V{x}, V{y}, V{z}); break;
 
+                case Op::mul_f32_imm: write(o, V{id}, "= mul_f32", V{x}, Splat{immy}); break;
+
                 case Op:: eq_f32: write(o, V{id}, "= eq_f32", V{x}, V{y}); break;
                 case Op::neq_f32: write(o, V{id}, "= neq_f32", V{x}, V{y}); break;
-                case Op:: lt_f32: write(o, V{id}, "= lt_f32", V{x}, V{y}); break;
-                case Op::lte_f32: write(o, V{id}, "= lte_f32", V{x}, V{y}); break;
                 case Op:: gt_f32: write(o, V{id}, "= gt_f32", V{x}, V{y}); break;
                 case Op::gte_f32: write(o, V{id}, "= gte_f32", V{x}, V{y}); break;
 
@@ -137,14 +187,12 @@
                 case Op::sub_i32: write(o, V{id}, "= sub_i32", V{x}, V{y}); break;
                 case Op::mul_i32: write(o, V{id}, "= mul_i32", V{x}, V{y}); break;
 
-                case Op::shl_i32: write(o, V{id}, "= shl_i32", V{x}, Shift{imm}); break;
-                case Op::shr_i32: write(o, V{id}, "= shr_i32", V{x}, Shift{imm}); break;
-                case Op::sra_i32: write(o, V{id}, "= sra_i32", V{x}, Shift{imm}); break;
+                case Op::shl_i32: write(o, V{id}, "= shl_i32", V{x}, Shift{immy}); break;
+                case Op::shr_i32: write(o, V{id}, "= shr_i32", V{x}, Shift{immy}); break;
+                case Op::sra_i32: write(o, V{id}, "= sra_i32", V{x}, Shift{immy}); break;
 
                 case Op:: eq_i32: write(o, V{id}, "= eq_i32", V{x}, V{y}); break;
                 case Op::neq_i32: write(o, V{id}, "= neq_i32", V{x}, V{y}); break;
-                case Op:: lt_i32: write(o, V{id}, "= lt_i32", V{x}, V{y}); break;
-                case Op::lte_i32: write(o, V{id}, "= lte_i32", V{x}, V{y}); break;
                 case Op:: gt_i32: write(o, V{id}, "= gt_i32", V{x}, V{y}); break;
                 case Op::gte_i32: write(o, V{id}, "= gte_i32", V{x}, V{y}); break;
 
@@ -152,14 +200,12 @@
                 case Op::sub_i16x2: write(o, V{id}, "= sub_i16x2", V{x}, V{y}); break;
                 case Op::mul_i16x2: write(o, V{id}, "= mul_i16x2", V{x}, V{y}); break;
 
-                case Op::shl_i16x2: write(o, V{id}, "= shl_i16x2", V{x}, Shift{imm}); break;
-                case Op::shr_i16x2: write(o, V{id}, "= shr_i16x2", V{x}, Shift{imm}); break;
-                case Op::sra_i16x2: write(o, V{id}, "= sra_i16x2", V{x}, Shift{imm}); break;
+                case Op::shl_i16x2: write(o, V{id}, "= shl_i16x2", V{x}, Shift{immy}); break;
+                case Op::shr_i16x2: write(o, V{id}, "= shr_i16x2", V{x}, Shift{immy}); break;
+                case Op::sra_i16x2: write(o, V{id}, "= sra_i16x2", V{x}, Shift{immy}); break;
 
                 case Op:: eq_i16x2: write(o, V{id}, "= eq_i16x2", V{x}, V{y}); break;
                 case Op::neq_i16x2: write(o, V{id}, "= neq_i16x2", V{x}, V{y}); break;
-                case Op:: lt_i16x2: write(o, V{id}, "= lt_i16x2", V{x}, V{y}); break;
-                case Op::lte_i16x2: write(o, V{id}, "= lte_i16x2", V{x}, V{y}); break;
                 case Op:: gt_i16x2: write(o, V{id}, "= gt_i16x2", V{x}, V{y}); break;
                 case Op::gte_i16x2: write(o, V{id}, "= gte_i16x2", V{x}, V{y}); break;
 
@@ -169,12 +215,13 @@
                 case Op::bit_clear: write(o, V{id}, "= bit_clear", V{x}, V{y}      ); break;
                 case Op::select   : write(o, V{id}, "= select"   , V{x}, V{y}, V{z}); break;
 
-                case Op::bytes:   write(o, V{id}, "= bytes",   V{x}, Hex{imm}); break;
-                case Op::extract: write(o, V{id}, "= extract", V{x}, Shift{imm}, V{y}); break;
-                case Op::pack:    write(o, V{id}, "= pack",    V{x}, V{y}, Shift{imm}); break;
+                case Op::bytes:   write(o, V{id}, "= bytes",   V{x}, Hex{immy}); break;
+                case Op::extract: write(o, V{id}, "= extract", V{x}, Shift{immy}, V{z}); break;
+                case Op::pack:    write(o, V{id}, "= pack",    V{x}, V{y}, Shift{immz}); break;
 
                 case Op::to_f32: write(o, V{id}, "= to_f32", V{x}); break;
-                case Op::to_i32: write(o, V{id}, "= to_i32", V{x}); break;
+                case Op::trunc:  write(o, V{id}, "= trunc",  V{x}); break;
+                case Op::round:  write(o, V{id}, "= round",  V{x}); break;
             }
 
             write(o, "\n");
@@ -199,46 +246,52 @@
         o->writeDecAsText(fInstructions.size());
         o->writeText(" instructions:\n");
         for (int i = 0; i < (int)fInstructions.size(); i++) {
-            if (i == fLoop) {
-                write(o, "loop:\n");
-            }
+            if (i == fLoop) { write(o, "loop:\n"); }
+            if (i >= fLoop) { write(o, "    "); }
             const Program::Instruction& inst = fInstructions[i];
             Op   op = inst.op;
             Reg   d = inst.d,
                   x = inst.x,
                   y = inst.y,
                   z = inst.z;
-            int imm = inst.imm;
+            int immy = inst.immy,
+                immz = inst.immz;
             switch (op) {
-                case Op::store8:  write(o, "store8" , Arg{imm}, R{x}); break;
-                case Op::store16: write(o, "store16", Arg{imm}, R{x}); break;
-                case Op::store32: write(o, "store32", Arg{imm}, R{x}); break;
+                case Op::assert_true:  write(o, "assert_true" , R{x}); break;
 
-                case Op::load8:  write(o, R{d}, "= load8" , Arg{imm}); break;
-                case Op::load16: write(o, R{d}, "= load16", Arg{imm}); break;
-                case Op::load32: write(o, R{d}, "= load32", Arg{imm}); break;
+                case Op::store8:  write(o, "store8" , Arg{immy}, R{x}); break;
+                case Op::store16: write(o, "store16", Arg{immy}, R{x}); break;
+                case Op::store32: write(o, "store32", Arg{immy}, R{x}); break;
 
-                case Op::gather8:  write(o, R{d}, "= gather8" , Arg{imm}, R{x}); break;
-                case Op::gather16: write(o, R{d}, "= gather16", Arg{imm}, R{x}); break;
-                case Op::gather32: write(o, R{d}, "= gather32", Arg{imm}, R{x}); break;
+                case Op::index: write(o, R{d}, "= index"); break;
 
-                case Op::uniform8:  write(o, R{d}, "= uniform8" , Arg{imm & 0xffff}, Hex{imm>>16}); break;
-                case Op::uniform16: write(o, R{d}, "= uniform16", Arg{imm & 0xffff}, Hex{imm>>16}); break;
-                case Op::uniform32: write(o, R{d}, "= uniform32", Arg{imm & 0xffff}, Hex{imm>>16}); break;
+                case Op::load8:  write(o, R{d}, "= load8" , Arg{immy}); break;
+                case Op::load16: write(o, R{d}, "= load16", Arg{immy}); break;
+                case Op::load32: write(o, R{d}, "= load32", Arg{immy}); break;
 
-                case Op::splat:  write(o, R{d}, "= splat", Splat{imm}); break;
+                case Op::gather8:  write(o, R{d}, "= gather8" , Arg{immy}, R{x}); break;
+                case Op::gather16: write(o, R{d}, "= gather16", Arg{immy}, R{x}); break;
+                case Op::gather32: write(o, R{d}, "= gather32", Arg{immy}, R{x}); break;
+
+                case Op::uniform8:  write(o, R{d}, "= uniform8" , Arg{immy}, Hex{immz}); break;
+                case Op::uniform16: write(o, R{d}, "= uniform16", Arg{immy}, Hex{immz}); break;
+                case Op::uniform32: write(o, R{d}, "= uniform32", Arg{immy}, Hex{immz}); break;
+
+                case Op::splat:  write(o, R{d}, "= splat", Splat{immy}); break;
 
 
                 case Op::add_f32: write(o, R{d}, "= add_f32", R{x}, R{y}      ); break;
                 case Op::sub_f32: write(o, R{d}, "= sub_f32", R{x}, R{y}      ); break;
                 case Op::mul_f32: write(o, R{d}, "= mul_f32", R{x}, R{y}      ); break;
                 case Op::div_f32: write(o, R{d}, "= div_f32", R{x}, R{y}      ); break;
+                case Op::min_f32: write(o, R{d}, "= min_f32", R{x}, R{y}      ); break;
+                case Op::max_f32: write(o, R{d}, "= max_f32", R{x}, R{y}      ); break;
                 case Op::mad_f32: write(o, R{d}, "= mad_f32", R{x}, R{y}, R{z}); break;
 
+                case Op::mul_f32_imm: write(o, R{d}, "= mul_f32", R{x}, Splat{immy}); break;
+
                 case Op:: eq_f32: write(o, R{d}, "= eq_f32", R{x}, R{y}); break;
                 case Op::neq_f32: write(o, R{d}, "= neq_f32", R{x}, R{y}); break;
-                case Op:: lt_f32: write(o, R{d}, "= lt_f32", R{x}, R{y}); break;
-                case Op::lte_f32: write(o, R{d}, "= lte_f32", R{x}, R{y}); break;
                 case Op:: gt_f32: write(o, R{d}, "= gt_f32", R{x}, R{y}); break;
                 case Op::gte_f32: write(o, R{d}, "= gte_f32", R{x}, R{y}); break;
 
@@ -247,14 +300,12 @@
                 case Op::sub_i32: write(o, R{d}, "= sub_i32", R{x}, R{y}); break;
                 case Op::mul_i32: write(o, R{d}, "= mul_i32", R{x}, R{y}); break;
 
-                case Op::shl_i32: write(o, R{d}, "= shl_i32", R{x}, Shift{imm}); break;
-                case Op::shr_i32: write(o, R{d}, "= shr_i32", R{x}, Shift{imm}); break;
-                case Op::sra_i32: write(o, R{d}, "= sra_i32", R{x}, Shift{imm}); break;
+                case Op::shl_i32: write(o, R{d}, "= shl_i32", R{x}, Shift{immy}); break;
+                case Op::shr_i32: write(o, R{d}, "= shr_i32", R{x}, Shift{immy}); break;
+                case Op::sra_i32: write(o, R{d}, "= sra_i32", R{x}, Shift{immy}); break;
 
                 case Op:: eq_i32: write(o, R{d}, "= eq_i32", R{x}, R{y}); break;
                 case Op::neq_i32: write(o, R{d}, "= neq_i32", R{x}, R{y}); break;
-                case Op:: lt_i32: write(o, R{d}, "= lt_i32", R{x}, R{y}); break;
-                case Op::lte_i32: write(o, R{d}, "= lte_i32", R{x}, R{y}); break;
                 case Op:: gt_i32: write(o, R{d}, "= gt_i32", R{x}, R{y}); break;
                 case Op::gte_i32: write(o, R{d}, "= gte_i32", R{x}, R{y}); break;
 
@@ -263,14 +314,12 @@
                 case Op::sub_i16x2: write(o, R{d}, "= sub_i16x2", R{x}, R{y}); break;
                 case Op::mul_i16x2: write(o, R{d}, "= mul_i16x2", R{x}, R{y}); break;
 
-                case Op::shl_i16x2: write(o, R{d}, "= shl_i16x2", R{x}, Shift{imm}); break;
-                case Op::shr_i16x2: write(o, R{d}, "= shr_i16x2", R{x}, Shift{imm}); break;
-                case Op::sra_i16x2: write(o, R{d}, "= sra_i16x2", R{x}, Shift{imm}); break;
+                case Op::shl_i16x2: write(o, R{d}, "= shl_i16x2", R{x}, Shift{immy}); break;
+                case Op::shr_i16x2: write(o, R{d}, "= shr_i16x2", R{x}, Shift{immy}); break;
+                case Op::sra_i16x2: write(o, R{d}, "= sra_i16x2", R{x}, Shift{immy}); break;
 
                 case Op:: eq_i16x2: write(o, R{d}, "= eq_i16x2", R{x}, R{y}); break;
                 case Op::neq_i16x2: write(o, R{d}, "= neq_i16x2", R{x}, R{y}); break;
-                case Op:: lt_i16x2: write(o, R{d}, "= lt_i16x2", R{x}, R{y}); break;
-                case Op::lte_i16x2: write(o, R{d}, "= lte_i16x2", R{x}, R{y}); break;
                 case Op:: gt_i16x2: write(o, R{d}, "= gt_i16x2", R{x}, R{y}); break;
                 case Op::gte_i16x2: write(o, R{d}, "= gte_i16x2", R{x}, R{y}); break;
 
@@ -281,12 +330,13 @@
                 case Op::bit_clear: write(o, R{d}, "= bit_clear", R{x}, R{y}      ); break;
                 case Op::select   : write(o, R{d}, "= select"   , R{x}, R{y}, R{z}); break;
 
-                case Op::bytes:   write(o, R{d}, "= bytes", R{x}, Hex{imm}); break;
-                case Op::extract: write(o, R{d}, "= extract", R{x}, Shift{imm}, R{y}); break;
-                case Op::pack:    write(o, R{d}, "= pack",    R{x}, R{y}, Shift{imm}); break;
+                case Op::bytes:   write(o, R{d}, "= bytes", R{x}, Hex{immy}); break;
+                case Op::extract: write(o, R{d}, "= extract", R{x}, Shift{immy}, R{z}); break;
+                case Op::pack:    write(o, R{d}, "= pack",    R{x}, R{y}, Shift{immz}); break;
 
                 case Op::to_f32: write(o, R{d}, "= to_f32", R{x}); break;
-                case Op::to_i32: write(o, R{d}, "= to_i32", R{x}); break;
+                case Op::trunc:  write(o, R{d}, "= trunc",  R{x}); break;
+                case Op::round:  write(o, R{d}, "= round",  R{x}); break;
             }
             write(o, "\n");
         }
@@ -364,7 +414,7 @@
             Builder::Instruction& inst = fProgram[id];
 
             // Varying loads (and gathers) and stores cannot be hoisted out of the loop.
-            if (inst.op <= Op::gather32) {
+            if (inst.op <= Op::gather32 && inst.op != Op::assert_true) {
                 inst.can_hoist = false;
             }
 
@@ -384,6 +434,12 @@
             }
         }
 
+        char buf[64] = "skvm-jit-";
+        if (!debug_name) {
+            *SkStrAppendU32(buf+9, this->hash()) = '\0';
+            debug_name = buf;
+        }
+
         return {fProgram, fStrides, debug_name};
     }
 
@@ -396,7 +452,8 @@
             && a.x            == b.x
             && a.y            == b.y
             && a.z            == b.z
-            && a.imm          == b.imm
+            && a.immy         == b.immy
+            && a.immz         == b.immz
             && a.death        == b.death
             && a.can_hoist    == b.can_hoist
             && a.used_in_loop == b.used_in_loop;
@@ -404,22 +461,32 @@
 
     // TODO: replace with SkOpts::hash()?
     size_t Builder::InstructionHash::operator()(const Instruction& inst) const {
-        return Hash((uint8_t)inst.op)
-            ^ Hash(inst.x)
-            ^ Hash(inst.y)
-            ^ Hash(inst.z)
-            ^ Hash(inst.imm)
-            ^ Hash(inst.death)
-            ^ Hash(inst.can_hoist)
-            ^ Hash(inst.used_in_loop);
+        auto hash = [](auto v) {
+            return std::hash<decltype(v)>{}(v);
+        };
+        return hash((uint8_t)inst.op)
+             ^ hash(inst.x)
+             ^ hash(inst.y)
+             ^ hash(inst.z)
+             ^ hash(inst.immy)
+             ^ hash(inst.immz)
+             ^ hash(inst.death)
+             ^ hash(inst.can_hoist)
+             ^ hash(inst.used_in_loop);
     }
 
+    uint32_t Builder::hash() const { return fHash; }
+
     // Most instructions produce a value and return it by ID,
     // the value-producing instruction's own index in the program vector.
-    Val Builder::push(Op op, Val x, Val y, Val z, int imm) {
-        Instruction inst{op, x, y, z, imm,
+    Val Builder::push(Op op, Val x, Val y, Val z, int immy, int immz) {
+        Instruction inst{op, x, y, z, immy, immz,
                          /*death=*/0, /*can_hoist=*/true, /*used_in_loop=*/false};
 
+        // This InstructionHash{}() call should be free given we're about to use fIndex below.
+        fHash ^= InstructionHash{}(inst);
+        fHash = SkChecksum::CheapMix(fHash);  // Make sure instruction order matters.
+
         // Basic common subexpression elimination:
         // if we've already seen this exact Instruction, use it instead of creating a new one.
         if (Val* id = fIndex.find(inst)) {
@@ -431,9 +498,16 @@
         return id;
     }
 
-    bool Builder::isZero(Val id) const {
-        return fProgram[id].op  == Op::splat
-            && fProgram[id].imm == 0;
+    bool Builder::allImm() const { return true; }
+
+    template <typename T, typename... Rest>
+    bool Builder::allImm(Val id, T* imm, Rest... rest) const {
+        if (fProgram[id].op == Op::splat) {
+            static_assert(sizeof(T) == 4);
+            memcpy(imm, &fProgram[id].immy, 4);
+            return this->allImm(rest...);
+        }
+        return false;
     }
 
     Arg Builder::arg(int stride) {
@@ -442,10 +516,20 @@
         return {ix};
     }
 
+    void Builder::assert_true(I32 val) {
+    #ifdef SK_DEBUG
+        int imm;
+        if (this->allImm(val.id,&imm)) { SkASSERT(imm); return; }
+        (void)this->push(Op::assert_true, val.id,NA,NA);
+    #endif
+    }
+
     void Builder::store8 (Arg ptr, I32 val) { (void)this->push(Op::store8 , val.id,NA,NA, ptr.ix); }
     void Builder::store16(Arg ptr, I32 val) { (void)this->push(Op::store16, val.id,NA,NA, ptr.ix); }
     void Builder::store32(Arg ptr, I32 val) { (void)this->push(Op::store32, val.id,NA,NA, ptr.ix); }
 
+    I32 Builder::index() { return {this->push(Op::index , NA,NA,NA,0) }; }
+
     I32 Builder::load8 (Arg ptr) { return {this->push(Op::load8 , NA,NA,NA, ptr.ix) }; }
     I32 Builder::load16(Arg ptr) { return {this->push(Op::load16, NA,NA,NA, ptr.ix) }; }
     I32 Builder::load32(Arg ptr) { return {this->push(Op::load32, NA,NA,NA, ptr.ix) }; }
@@ -461,13 +545,13 @@
     }
 
     I32 Builder::uniform8(Arg ptr, int offset) {
-        return {this->push(Op::uniform8, NA,NA,NA, ptr.ix | (offset<<16))};
+        return {this->push(Op::uniform8, NA,NA,NA, ptr.ix, offset)};
     }
     I32 Builder::uniform16(Arg ptr, int offset) {
-        return {this->push(Op::uniform16, NA,NA,NA, ptr.ix | (offset<<16))};
+        return {this->push(Op::uniform16, NA,NA,NA, ptr.ix, offset)};
     }
     I32 Builder::uniform32(Arg ptr, int offset) {
-        return {this->push(Op::uniform32, NA,NA,NA, ptr.ix | (offset<<16))};
+        return {this->push(Op::uniform32, NA,NA,NA, ptr.ix, offset)};
     }
 
     // The two splat() functions are just syntax sugar over splatting a 4-byte bit pattern.
@@ -478,17 +562,76 @@
         return {this->push(Op::splat, NA,NA,NA, bits)};
     }
 
-    F32 Builder::add(F32 x, F32 y       ) { return {this->push(Op::add_f32, x.id, y.id)}; }
-    F32 Builder::sub(F32 x, F32 y       ) { return {this->push(Op::sub_f32, x.id, y.id)}; }
-    F32 Builder::mul(F32 x, F32 y       ) { return {this->push(Op::mul_f32, x.id, y.id)}; }
-    F32 Builder::div(F32 x, F32 y       ) { return {this->push(Op::div_f32, x.id, y.id)}; }
+    // Be careful peepholing float math!  Transformations you might expect to
+    // be legal can fail in the face of NaN/Inf, e.g. 0*x is not always 0.
+    // Float peepholes must pass this equivalence test for all ~4B floats:
+    //
+    //     bool equiv(float x, float y) { return (x == y) || (isnanf(x) && isnanf(y)); }
+    //
+    //     unsigned bits = 0;
+    //     do {
+    //        float f;
+    //        memcpy(&f, &bits, 4);
+    //        if (!equiv(f, ...)) {
+    //           abort();
+    //        }
+    //     } while (++bits != 0);
+
+    F32 Builder::add(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X+Y); }
+        if (this->isImm(y.id, 0.0f)) { return x; }   // x+0 == x
+        if (this->isImm(x.id, 0.0f)) { return y; }   // 0+y == y
+        return {this->push(Op::add_f32, x.id, y.id)};
+    }
+
+    F32 Builder::sub(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X-Y); }
+        if (this->isImm(y.id, 0.0f)) { return x; }   // x-0 == x
+        return {this->push(Op::sub_f32, x.id, y.id)};
+    }
+
+    F32 Builder::mul(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X*Y); }
+        if (this->isImm(y.id, 1.0f)) { return x; }  // x*1 == x
+        if (this->isImm(x.id, 1.0f)) { return y; }  // 1*y == y
+    #if defined(SK_CPU_X86)
+        int imm;
+        if (this->allImm(y.id, &imm)) { return {this->push(Op::mul_f32_imm, x.id,NA,NA, imm)}; }
+        if (this->allImm(x.id, &imm)) { return {this->push(Op::mul_f32_imm, y.id,NA,NA, imm)}; }
+    #endif
+        return {this->push(Op::mul_f32, x.id, y.id)};
+    }
+
+    F32 Builder::div(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X/Y); }
+        if (this->isImm(y.id, 1.0f)) { return x; }  // x/1 == x
+        return {this->push(Op::div_f32, x.id, y.id)};
+    }
+
     F32 Builder::mad(F32 x, F32 y, F32 z) {
-        if (this->isZero(z.id)) {
-            return this->mul(x,y);
-        }
+        float X,Y,Z;
+        if (this->allImm(x.id,&X, y.id,&Y, z.id,&Z)) { return this->splat(X*Y+Z); }
+        if (this->isImm(y.id, 1.0f)) { return this->add(x,z); }  // x*1+z == x+z
+        if (this->isImm(x.id, 1.0f)) { return this->add(y,z); }  // 1*y+z == y+z
+        if (this->isImm(z.id, 0.0f)) { return this->mul(x,y); }  // x*y+0 == x*y
         return {this->push(Op::mad_f32, x.id, y.id, z.id)};
     }
 
+    F32 Builder::min(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(std::min(X,Y)); }
+        return {this->push(Op::min_f32, x.id, y.id)};
+    }
+    F32 Builder::max(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(std::max(X,Y)); }
+        return {this->push(Op::max_f32, x.id, y.id)};
+    }
+
     I32 Builder::add(I32 x, I32 y) { return {this->push(Op::add_i32, x.id, y.id)}; }
     I32 Builder::sub(I32 x, I32 y) { return {this->push(Op::sub_i32, x.id, y.id)}; }
     I32 Builder::mul(I32 x, I32 y) { return {this->push(Op::mul_i32, x.id, y.id)}; }
@@ -497,56 +640,129 @@
     I32 Builder::sub_16x2(I32 x, I32 y) { return {this->push(Op::sub_i16x2, x.id, y.id)}; }
     I32 Builder::mul_16x2(I32 x, I32 y) { return {this->push(Op::mul_i16x2, x.id, y.id)}; }
 
-    I32 Builder::shl(I32 x, int bits) { return {this->push(Op::shl_i32, x.id,NA,NA, bits)}; }
-    I32 Builder::shr(I32 x, int bits) { return {this->push(Op::shr_i32, x.id,NA,NA, bits)}; }
-    I32 Builder::sra(I32 x, int bits) { return {this->push(Op::sra_i32, x.id,NA,NA, bits)}; }
+    I32 Builder::shl(I32 x, int bits) {
+        int X;
+        if (this->allImm(x.id,&X)) { return this->splat(X << bits); }
+        return {this->push(Op::shl_i32, x.id,NA,NA, bits)};
+    }
+    I32 Builder::shr(I32 x, int bits) {
+        int X;
+        if (this->allImm(x.id,&X)) { return this->splat(unsigned(X) >> bits); }
+        return {this->push(Op::shr_i32, x.id,NA,NA, bits)};
+    }
+    I32 Builder::sra(I32 x, int bits) {
+        int X;
+        if (this->allImm(x.id,&X)) { return this->splat(X >> bits); }
+        return {this->push(Op::sra_i32, x.id,NA,NA, bits)};
+    }
 
     I32 Builder::shl_16x2(I32 x, int bits) { return {this->push(Op::shl_i16x2, x.id,NA,NA, bits)}; }
     I32 Builder::shr_16x2(I32 x, int bits) { return {this->push(Op::shr_i16x2, x.id,NA,NA, bits)}; }
     I32 Builder::sra_16x2(I32 x, int bits) { return {this->push(Op::sra_i16x2, x.id,NA,NA, bits)}; }
 
-    I32 Builder:: eq(F32 x, F32 y) { return {this->push(Op:: eq_f32, x.id, y.id)}; }
-    I32 Builder::neq(F32 x, F32 y) { return {this->push(Op::neq_f32, x.id, y.id)}; }
-    I32 Builder:: lt(F32 x, F32 y) { return {this->push(Op:: lt_f32, x.id, y.id)}; }
-    I32 Builder::lte(F32 x, F32 y) { return {this->push(Op::lte_f32, x.id, y.id)}; }
-    I32 Builder:: gt(F32 x, F32 y) { return {this->push(Op:: gt_f32, x.id, y.id)}; }
-    I32 Builder::gte(F32 x, F32 y) { return {this->push(Op::gte_f32, x.id, y.id)}; }
+    I32 Builder:: eq(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X==Y ? ~0 : 0); }
+        return {this->push(Op::eq_f32, x.id, y.id)};
+    }
+    I32 Builder::neq(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X!=Y ? ~0 : 0); }
+        return {this->push(Op::neq_f32, x.id, y.id)};
+    }
+    I32 Builder::lt(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(Y> X ? ~0 : 0); }
+        return {this->push(Op::gt_f32, y.id, x.id)};
+    }
+    I32 Builder::lte(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(Y>=X ? ~0 : 0); }
+        return {this->push(Op::gte_f32, y.id, x.id)};
+    }
+    I32 Builder::gt(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X> Y ? ~0 : 0); }
+        return {this->push(Op::gt_f32, x.id, y.id)};
+    }
+    I32 Builder::gte(F32 x, F32 y) {
+        float X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X>=Y ? ~0 : 0); }
+        return {this->push(Op::gte_f32, x.id, y.id)};
+    }
 
     I32 Builder:: eq(I32 x, I32 y) { return {this->push(Op:: eq_i32, x.id, y.id)}; }
     I32 Builder::neq(I32 x, I32 y) { return {this->push(Op::neq_i32, x.id, y.id)}; }
-    I32 Builder:: lt(I32 x, I32 y) { return {this->push(Op:: lt_i32, x.id, y.id)}; }
-    I32 Builder::lte(I32 x, I32 y) { return {this->push(Op::lte_i32, x.id, y.id)}; }
+    I32 Builder:: lt(I32 x, I32 y) { return {this->push(Op:: gt_i32, y.id, x.id)}; }
+    I32 Builder::lte(I32 x, I32 y) { return {this->push(Op::gte_i32, y.id, x.id)}; }
     I32 Builder:: gt(I32 x, I32 y) { return {this->push(Op:: gt_i32, x.id, y.id)}; }
     I32 Builder::gte(I32 x, I32 y) { return {this->push(Op::gte_i32, x.id, y.id)}; }
 
     I32 Builder:: eq_16x2(I32 x, I32 y) { return {this->push(Op:: eq_i16x2, x.id, y.id)}; }
     I32 Builder::neq_16x2(I32 x, I32 y) { return {this->push(Op::neq_i16x2, x.id, y.id)}; }
-    I32 Builder:: lt_16x2(I32 x, I32 y) { return {this->push(Op:: lt_i16x2, x.id, y.id)}; }
-    I32 Builder::lte_16x2(I32 x, I32 y) { return {this->push(Op::lte_i16x2, x.id, y.id)}; }
+    I32 Builder:: lt_16x2(I32 x, I32 y) { return {this->push(Op:: gt_i16x2, y.id, x.id)}; }
+    I32 Builder::lte_16x2(I32 x, I32 y) { return {this->push(Op::gte_i16x2, y.id, x.id)}; }
     I32 Builder:: gt_16x2(I32 x, I32 y) { return {this->push(Op:: gt_i16x2, x.id, y.id)}; }
     I32 Builder::gte_16x2(I32 x, I32 y) { return {this->push(Op::gte_i16x2, x.id, y.id)}; }
 
-    I32 Builder::bit_and  (I32 x, I32 y) { return {this->push(Op::bit_and  , x.id, y.id)}; }
-    I32 Builder::bit_or   (I32 x, I32 y) { return {this->push(Op::bit_or   , x.id, y.id)}; }
-    I32 Builder::bit_xor  (I32 x, I32 y) { return {this->push(Op::bit_xor  , x.id, y.id)}; }
-    I32 Builder::bit_clear(I32 x, I32 y) { return {this->push(Op::bit_clear, x.id, y.id)}; }
-    I32 Builder::select(I32 x, I32 y, I32 z) { return {this->push(Op::select, x.id, y.id, z.id)}; }
+    I32 Builder::bit_and(I32 x, I32 y) {
+        int X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X&Y); }
+        return {this->push(Op::bit_and, x.id, y.id)};
+    }
+    I32 Builder::bit_or(I32 x, I32 y) {
+        int X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X|Y); }
+        return {this->push(Op::bit_or, x.id, y.id)};
+    }
+    I32 Builder::bit_xor(I32 x, I32 y) {
+        int X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X^Y); }
+        return {this->push(Op::bit_xor, x.id, y.id)};
+    }
+    I32 Builder::bit_clear(I32 x, I32 y) {
+        int X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X&~Y); }
+        return {this->push(Op::bit_clear, x.id, y.id)};
+    }
+    I32 Builder::select(I32 x, I32 y, I32 z) {
+        int X,Y,Z;
+        if (this->allImm(x.id,&X, y.id,&Y, z.id,&Z)) { return this->splat(X?Y:Z); }
+        return {this->push(Op::select, x.id, y.id, z.id)};
+    }
 
 
-    I32 Builder::extract(I32 x, int bits, I32 y) {
-        return {this->push(Op::extract, x.id,y.id,NA, bits)};
+    I32 Builder::extract(I32 x, int bits, I32 z) {
+        int X,Z;
+        if (this->allImm(x.id,&X, z.id,&Z)) { return this->splat( (unsigned(X)>>bits)&Z ); }
+        return {this->push(Op::extract, x.id,NA,z.id, bits,0)};
     }
 
     I32 Builder::pack(I32 x, I32 y, int bits) {
-        return {this->push(Op::pack, x.id,y.id,NA, bits)};
+        int X,Y;
+        if (this->allImm(x.id,&X, y.id,&Y)) { return this->splat(X|(Y<<bits)); }
+        return {this->push(Op::pack, x.id,y.id,NA, 0,bits)};
     }
 
     I32 Builder::bytes(I32 x, int control) {
         return {this->push(Op::bytes, x.id,NA,NA, control)};
     }
 
-    F32 Builder::to_f32(I32 x) { return {this->push(Op::to_f32, x.id)}; }
-    I32 Builder::to_i32(F32 x) { return {this->push(Op::to_i32, x.id)}; }
+    F32 Builder::to_f32(I32 x) {
+        int X;
+        if (this->allImm(x.id,&X)) { return this->splat((float)X); }
+        return {this->push(Op::to_f32, x.id)};
+    }
+    I32 Builder::trunc(F32 x) {
+        float X;
+        if (this->allImm(x.id,&X)) { return this->splat((int)X); }
+        return {this->push(Op::trunc, x.id)};
+    }
+    I32 Builder::round(F32 x) {
+        float X;
+        if (this->allImm(x.id,&X)) { return this->splat((int)lrintf(X)); }
+        return {this->push(Op::round, x.id)};
+    }
 
     // ~~~~ Program::eval() and co. ~~~~ //
 
@@ -688,6 +904,10 @@
         }
     }
 
+    void Assembler::int3() {
+        this->byte(0xcc);
+    }
+
     void Assembler::vzeroupper() {
         this->byte(0xc5);
         this->byte(0xf8);
@@ -740,6 +960,8 @@
     void Assembler::vsubps(Ymm dst, Ymm x, Ymm y) { this->op(0,0x0f,0x5c, dst,x,y); }
     void Assembler::vmulps(Ymm dst, Ymm x, Ymm y) { this->op(0,0x0f,0x59, dst,x,y); }
     void Assembler::vdivps(Ymm dst, Ymm x, Ymm y) { this->op(0,0x0f,0x5e, dst,x,y); }
+    void Assembler::vminps(Ymm dst, Ymm x, Ymm y) { this->op(0,0x0f,0x5d, dst,x,y); }
+    void Assembler::vmaxps(Ymm dst, Ymm x, Ymm y) { this->op(0,0x0f,0x5f, dst,x,y); }
 
     void Assembler::vfmadd132ps(Ymm dst, Ymm x, Ymm y) { this->op(0x66,0x380f,0x98, dst,x,y); }
     void Assembler::vfmadd213ps(Ymm dst, Ymm x, Ymm y) { this->op(0x66,0x380f,0xa8, dst,x,y); }
@@ -751,6 +973,11 @@
     void Assembler::vpcmpeqd(Ymm dst, Ymm x, Ymm y) { this->op(0x66,0x0f,0x76, dst,x,y); }
     void Assembler::vpcmpgtd(Ymm dst, Ymm x, Ymm y) { this->op(0x66,0x0f,0x66, dst,x,y); }
 
+    void Assembler::vcmpps(Ymm dst, Ymm x, Ymm y, int imm) {
+        this->op(0,0x0f,0xc2, dst,x,y);
+        this->byte(imm);
+    }
+
     void Assembler::vpblendvb(Ymm dst, Ymm x, Ymm y, Ymm z) {
         int prefix = 0x66,
             map    = 0x3a0f,
@@ -789,13 +1016,14 @@
 
     void Assembler::vcvtdq2ps (Ymm dst, Ymm x) { this->op(0,   0x0f,0x5b, dst,x); }
     void Assembler::vcvttps2dq(Ymm dst, Ymm x) { this->op(0xf3,0x0f,0x5b, dst,x); }
+    void Assembler::vcvtps2dq (Ymm dst, Ymm x) { this->op(0x66,0x0f,0x5b, dst,x); }
 
     Assembler::Label Assembler::here() {
-        return { (int)this->size(), Label::None, {} };
+        return { (int)this->size(), Label::NotYetSet, {} };
     }
 
     int Assembler::disp19(Label* l) {
-        SkASSERT(l->kind == Label::None ||
+        SkASSERT(l->kind == Label::NotYetSet ||
                  l->kind == Label::ARMDisp19);
         l->kind = Label::ARMDisp19;
         l->references.push_back(here().offset);
@@ -804,7 +1032,7 @@
     }
 
     int Assembler::disp32(Label* l) {
-        SkASSERT(l->kind == Label::None ||
+        SkASSERT(l->kind == Label::NotYetSet ||
                  l->kind == Label::X86Disp32);
         l->kind = Label::X86Disp32;
         l->references.push_back(here().offset);
@@ -825,6 +1053,11 @@
     }
 
     void Assembler::vpshufb(Ymm dst, Ymm x, Label* l) { this->op(0x66,0x380f,0x00, dst,x,l); }
+    void Assembler::vpaddd (Ymm dst, Ymm x, Label* l) { this->op(0x66,  0x0f,0xfe, dst,x,l); }
+    void Assembler::vpsubd (Ymm dst, Ymm x, Label* l) { this->op(0x66,  0x0f,0xfa, dst,x,l); }
+    void Assembler::vmulps (Ymm dst, Ymm x, Label* l) { this->op(   0,  0x0f,0x59, dst,x,l); }
+
+    void Assembler::vptest(Ymm dst, Label* l) { this->op(0x66, 0x380f, 0x17, dst, (Ymm)0, l); }
 
     void Assembler::vbroadcastss(Ymm dst, Label* l) { this->op(0x66,0x380f,0x18, dst, (Ymm)0, l); }
     void Assembler::vbroadcastss(Ymm dst, Xmm src)  { this->op(0x66,0x380f,0x18, dst, (Ymm)src); }
@@ -853,6 +1086,7 @@
     void Assembler::je (Label* l) { this->jump(0x84, l); }
     void Assembler::jne(Label* l) { this->jump(0x85, l); }
     void Assembler::jl (Label* l) { this->jump(0x8c, l); }
+    void Assembler::jc (Label* l) { this->jump(0x82, l); }
 
     void Assembler::jmp(Label* l) {
         // Like above in jump(), we could use 8-bit displacement here, but always use 32-bit.
@@ -1025,6 +1259,7 @@
     void Assembler::eor16b(V d, V n, V m) { this->op(0b0'1'1'01110'00'1, m, 0b00011'1, n, d); }
     void Assembler::bic16b(V d, V n, V m) { this->op(0b0'1'0'01110'01'1, m, 0b00011'1, n, d); }
     void Assembler::bsl16b(V d, V n, V m) { this->op(0b0'1'1'01110'01'1, m, 0b00011'1, n, d); }
+    void Assembler::not16b(V d, V n)      { this->op(0b0'1'1'01110'00'10000'00101'10,  n, d); }
 
     void Assembler::add4s(V d, V n, V m) { this->op(0b0'1'0'01110'10'1, m, 0b10000'1, n, d); }
     void Assembler::sub4s(V d, V n, V m) { this->op(0b0'1'1'01110'10'1, m, 0b10000'1, n, d); }
@@ -1040,6 +1275,12 @@
     void Assembler::fsub4s(V d, V n, V m) { this->op(0b0'1'0'01110'1'0'1, m, 0b11010'1, n, d); }
     void Assembler::fmul4s(V d, V n, V m) { this->op(0b0'1'1'01110'0'0'1, m, 0b11011'1, n, d); }
     void Assembler::fdiv4s(V d, V n, V m) { this->op(0b0'1'1'01110'0'0'1, m, 0b11111'1, n, d); }
+    void Assembler::fmin4s(V d, V n, V m) { this->op(0b0'1'0'01110'1'0'1, m, 0b11110'1, n, d); }
+    void Assembler::fmax4s(V d, V n, V m) { this->op(0b0'1'0'01110'0'0'1, m, 0b11110'1, n, d); }
+
+    void Assembler::fcmeq4s(V d, V n, V m) { this->op(0b0'1'0'01110'0'0'1, m, 0b1110'0'1, n, d); }
+    void Assembler::fcmgt4s(V d, V n, V m) { this->op(0b0'1'1'01110'1'0'1, m, 0b1110'0'1, n, d); }
+    void Assembler::fcmge4s(V d, V n, V m) { this->op(0b0'1'1'01110'0'0'1, m, 0b1110'0'1, n, d); }
 
     void Assembler::fmla4s(V d, V n, V m) { this->op(0b0'1'0'01110'0'0'1, m, 0b11001'1, n, d); }
 
@@ -1070,6 +1311,7 @@
 
     void Assembler::scvtf4s (V d, V n) { this->op(0b0'1'0'01110'0'0'10000'11101'10, n,d); }
     void Assembler::fcvtzs4s(V d, V n) { this->op(0b0'1'0'01110'1'0'10000'1101'1'10, n,d); }
+    void Assembler::fcvtns4s(V d, V n) { this->op(0b0'1'0'01110'0'0'10000'1101'0'10, n,d); }
 
     void Assembler::xtns2h(V d, V n) { this->op(0b0'0'0'01110'01'10000'10010'10, n,d); }
     void Assembler::xtnh2b(V d, V n) { this->op(0b0'0'0'01110'00'10000'10010'10, n,d); }
@@ -1077,6 +1319,13 @@
     void Assembler::uxtlb2h(V d, V n) { this->op(0b0'0'1'011110'0001'000'10100'1, n,d); }
     void Assembler::uxtlh2s(V d, V n) { this->op(0b0'0'1'011110'0010'000'10100'1, n,d); }
 
+    void Assembler::uminv4s(V d, V n) { this->op(0b0'1'1'01110'10'11000'1'1010'10, n,d); }
+
+    void Assembler::brk(int imm16) {
+        this->word(0b11010100'001'0000000000000000'000'00
+                  | (imm16 & 16_mask) << 5);
+    }
+
     void Assembler::ret(X n) {
         this->word(0b1101011'0'0'10'11111'0000'0'0 << 10
                   | (n & 5_mask) << 5);
@@ -1128,6 +1377,12 @@
     void Assembler::strs(V src, X dst) { this->op(0b10'111'1'01'00'000000000000, dst, src); }
     void Assembler::strb(V src, X dst) { this->op(0b00'111'1'01'00'000000000000, dst, src); }
 
+    void Assembler::fmovs(X dst, V src) {
+        this->word(0b0'0'0'11110'00'1'00'110'000000 << 10
+                  | (src & 5_mask)                  << 5
+                  | (dst & 5_mask)                  << 0);
+    }
+
     void Assembler::ldrq(V dst, Label* l) {
         const int imm19 = this->disp19(l);
         this->word( 0b10'011'1'00     << 24
@@ -1187,6 +1442,8 @@
                 case 2: return ((void(*)(int,void*,void*            ))b)(n,a[0],a[1]          );
                 case 3: return ((void(*)(int,void*,void*,void*      ))b)(n,a[0],a[1],a[2]     );
                 case 4: return ((void(*)(int,void*,void*,void*,void*))b)(n,a[0],a[1],a[2],a[3]);
+                case 5: return ((void(*)(int,void*,void*,void*,void*,void*))b)
+                                (n,a[0],a[1],a[2],a[3],a[4]);
                 default: SkUNREACHABLE;  // TODO
             }
         }
@@ -1253,12 +1510,13 @@
             for (int i = start; i < (int)fInstructions.size(); i++) {
                 Instruction inst = fInstructions[i];
 
-                // d = op(x,y,z/imm)
+                // d = op(x,y/imm,z/imm)
                 Reg   d = inst.d,
                       x = inst.x,
                       y = inst.y,
                       z = inst.z;
-                int imm = inst.imm;
+                int immy = inst.immy,
+                    immz = inst.immz;
 
                 // Ops that interact with memory need to know whether we're stride=1 or K,
                 // but all non-memory ops can run the same code no matter the stride.
@@ -1267,46 +1525,46 @@
 
                 #define STRIDE_1(op) case 2*(int)op
                 #define STRIDE_K(op) case 2*(int)op + 1
-                    STRIDE_1(Op::store8 ): memcpy(arg(imm), &r(x).i32, 1); break;
-                    STRIDE_1(Op::store16): memcpy(arg(imm), &r(x).i32, 2); break;
-                    STRIDE_1(Op::store32): memcpy(arg(imm), &r(x).i32, 4); break;
+                    STRIDE_1(Op::store8 ): memcpy(arg(immy), &r(x).i32, 1); break;
+                    STRIDE_1(Op::store16): memcpy(arg(immy), &r(x).i32, 2); break;
+                    STRIDE_1(Op::store32): memcpy(arg(immy), &r(x).i32, 4); break;
 
-                    STRIDE_K(Op::store8 ): skvx::cast<uint8_t> (r(x).i32).store(arg(imm)); break;
-                    STRIDE_K(Op::store16): skvx::cast<uint16_t>(r(x).i32).store(arg(imm)); break;
-                    STRIDE_K(Op::store32):                     (r(x).i32).store(arg(imm)); break;
+                    STRIDE_K(Op::store8 ): skvx::cast<uint8_t> (r(x).i32).store(arg(immy)); break;
+                    STRIDE_K(Op::store16): skvx::cast<uint16_t>(r(x).i32).store(arg(immy)); break;
+                    STRIDE_K(Op::store32):                     (r(x).i32).store(arg(immy)); break;
 
-                    STRIDE_1(Op::load8 ): r(d).i32 = 0; memcpy(&r(d).i32, arg(imm), 1); break;
-                    STRIDE_1(Op::load16): r(d).i32 = 0; memcpy(&r(d).i32, arg(imm), 2); break;
-                    STRIDE_1(Op::load32): r(d).i32 = 0; memcpy(&r(d).i32, arg(imm), 4); break;
+                    STRIDE_1(Op::load8 ): r(d).i32 = 0; memcpy(&r(d).i32, arg(immy), 1); break;
+                    STRIDE_1(Op::load16): r(d).i32 = 0; memcpy(&r(d).i32, arg(immy), 2); break;
+                    STRIDE_1(Op::load32): r(d).i32 = 0; memcpy(&r(d).i32, arg(immy), 4); break;
 
-                    STRIDE_K(Op::load8 ): r(d).i32= skvx::cast<int>(U8 ::Load(arg(imm))); break;
-                    STRIDE_K(Op::load16): r(d).i32= skvx::cast<int>(U16::Load(arg(imm))); break;
-                    STRIDE_K(Op::load32): r(d).i32=                 I32::Load(arg(imm)) ; break;
+                    STRIDE_K(Op::load8 ): r(d).i32= skvx::cast<int>(U8 ::Load(arg(immy))); break;
+                    STRIDE_K(Op::load16): r(d).i32= skvx::cast<int>(U16::Load(arg(immy))); break;
+                    STRIDE_K(Op::load32): r(d).i32=                 I32::Load(arg(immy)) ; break;
 
                     STRIDE_1(Op::gather8):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = (i == 0) ? ((const uint8_t* )arg(imm))[ r(x).i32[i] ] : 0;
+                            r(d).i32[i] = (i==0) ? ((const uint8_t* )arg(immy))[ r(x).i32[i] ] : 0;
                         } break;
                     STRIDE_1(Op::gather16):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = (i == 0) ? ((const uint16_t*)arg(imm))[ r(x).i32[i] ] : 0;
+                            r(d).i32[i] = (i==0) ? ((const uint16_t*)arg(immy))[ r(x).i32[i] ] : 0;
                         } break;
                     STRIDE_1(Op::gather32):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = (i == 0) ? ((const int*     )arg(imm))[ r(x).i32[i] ] : 0;
+                            r(d).i32[i] = (i==0) ? ((const int*     )arg(immy))[ r(x).i32[i] ] : 0;
                         } break;
 
                     STRIDE_K(Op::gather8):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = ((const uint8_t* )arg(imm))[ r(x).i32[i] ];
+                            r(d).i32[i] = ((const uint8_t* )arg(immy))[ r(x).i32[i] ];
                         } break;
                     STRIDE_K(Op::gather16):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = ((const uint16_t*)arg(imm))[ r(x).i32[i] ];
+                            r(d).i32[i] = ((const uint16_t*)arg(immy))[ r(x).i32[i] ];
                         } break;
                     STRIDE_K(Op::gather32):
                         for (int i = 0; i < K; i++) {
-                            r(d).i32[i] = ((const int*     )arg(imm))[ r(x).i32[i] ];
+                            r(d).i32[i] = ((const int*     )arg(immy))[ r(x).i32[i] ];
                         } break;
 
                 #undef STRIDE_1
@@ -1315,22 +1573,36 @@
                     // Ops that don't interact with memory should never care about the stride.
                 #define CASE(op) case 2*(int)op: /*fallthrough*/ case 2*(int)op+1
 
+                    CASE(Op::assert_true): SkASSERT(all(r(x).i32)); break;
+
+                    CASE(Op::index): static_assert(K == 16, "");
+                                     r(d).i32 = n - I32{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+                                     break;
+
                     CASE(Op::uniform8):
-                        r(d).i32 = *(const uint8_t* )( (const char*)arg(imm&0xffff) + (imm>>16) );
+                        r(d).i32 = *(const uint8_t* )( (const char*)arg(immy) + immz );
                         break;
                     CASE(Op::uniform16):
-                        r(d).i32 = *(const uint16_t*)( (const char*)arg(imm&0xffff) + (imm>>16) );
+                        r(d).i32 = *(const uint16_t*)( (const char*)arg(immy) + immz );
                         break;
                     CASE(Op::uniform32):
-                        r(d).i32 = *(const int*     )( (const char*)arg(imm&0xffff) + (imm>>16) );
+                        r(d).i32 = *(const int*     )( (const char*)arg(immy) + immz );
                         break;
 
-                    CASE(Op::splat): r(d).i32 = imm; break;
+                    CASE(Op::splat): r(d).i32 = immy; break;
 
                     CASE(Op::add_f32): r(d).f32 = r(x).f32 + r(y).f32; break;
                     CASE(Op::sub_f32): r(d).f32 = r(x).f32 - r(y).f32; break;
                     CASE(Op::mul_f32): r(d).f32 = r(x).f32 * r(y).f32; break;
                     CASE(Op::div_f32): r(d).f32 = r(x).f32 / r(y).f32; break;
+                    CASE(Op::min_f32): r(d).f32 = min(r(x).f32, r(y).f32); break;
+                    CASE(Op::max_f32): r(d).f32 = max(r(x).f32, r(y).f32); break;
+
+                    CASE(Op::mul_f32_imm): {
+                        Slot tmp;
+                        tmp.i32 = immy;
+                        r(d).f32 = r(x).f32 * tmp.f32;
+                    } break;
 
                     CASE(Op::mad_f32): r(d).f32 = r(x).f32 * r(y).f32 + r(z).f32; break;
 
@@ -1342,32 +1614,26 @@
                     CASE(Op::sub_i16x2): r(d).i16x2 = r(x).i16x2 - r(y).i16x2; break;
                     CASE(Op::mul_i16x2): r(d).i16x2 = r(x).i16x2 * r(y).i16x2; break;
 
-                    CASE(Op::shl_i32): r(d).i32 = r(x).i32 << imm; break;
-                    CASE(Op::sra_i32): r(d).i32 = r(x).i32 >> imm; break;
-                    CASE(Op::shr_i32): r(d).u32 = r(x).u32 >> imm; break;
+                    CASE(Op::shl_i32): r(d).i32 = r(x).i32 << immy; break;
+                    CASE(Op::sra_i32): r(d).i32 = r(x).i32 >> immy; break;
+                    CASE(Op::shr_i32): r(d).u32 = r(x).u32 >> immy; break;
 
-                    CASE(Op::shl_i16x2): r(d).i16x2 = r(x).i16x2 << imm; break;
-                    CASE(Op::sra_i16x2): r(d).i16x2 = r(x).i16x2 >> imm; break;
-                    CASE(Op::shr_i16x2): r(d).u16x2 = r(x).u16x2 >> imm; break;
+                    CASE(Op::shl_i16x2): r(d).i16x2 = r(x).i16x2 << immy; break;
+                    CASE(Op::sra_i16x2): r(d).i16x2 = r(x).i16x2 >> immy; break;
+                    CASE(Op::shr_i16x2): r(d).u16x2 = r(x).u16x2 >> immy; break;
 
                     CASE(Op:: eq_f32): r(d).i32 = r(x).f32 == r(y).f32; break;
                     CASE(Op::neq_f32): r(d).i32 = r(x).f32 != r(y).f32; break;
-                    CASE(Op:: lt_f32): r(d).i32 = r(x).f32 <  r(y).f32; break;
-                    CASE(Op::lte_f32): r(d).i32 = r(x).f32 <= r(y).f32; break;
                     CASE(Op:: gt_f32): r(d).i32 = r(x).f32 >  r(y).f32; break;
                     CASE(Op::gte_f32): r(d).i32 = r(x).f32 >= r(y).f32; break;
 
                     CASE(Op:: eq_i32): r(d).i32 = r(x).i32 == r(y).i32; break;
                     CASE(Op::neq_i32): r(d).i32 = r(x).i32 != r(y).i32; break;
-                    CASE(Op:: lt_i32): r(d).i32 = r(x).i32 <  r(y).i32; break;
-                    CASE(Op::lte_i32): r(d).i32 = r(x).i32 <= r(y).i32; break;
                     CASE(Op:: gt_i32): r(d).i32 = r(x).i32 >  r(y).i32; break;
                     CASE(Op::gte_i32): r(d).i32 = r(x).i32 >= r(y).i32; break;
 
                     CASE(Op:: eq_i16x2): r(d).i16x2 = r(x).i16x2 == r(y).i16x2; break;
                     CASE(Op::neq_i16x2): r(d).i16x2 = r(x).i16x2 != r(y).i16x2; break;
-                    CASE(Op:: lt_i16x2): r(d).i16x2 = r(x).i16x2 <  r(y).i16x2; break;
-                    CASE(Op::lte_i16x2): r(d).i16x2 = r(x).i16x2 <= r(y).i16x2; break;
                     CASE(Op:: gt_i16x2): r(d).i16x2 = r(x).i16x2 >  r(y).i16x2; break;
                     CASE(Op::gte_i16x2): r(d).i16x2 = r(x).i16x2 >= r(y).i16x2; break;
 
@@ -1380,8 +1646,8 @@
                                       break;
 
 
-                    CASE(Op::extract): r(d).u32 = (r(x).u32 >> imm) & r(y).u32; break;
-                    CASE(Op::pack):    r(d).u32 = r(x).u32 | (r(y).u32 << imm); break;
+                    CASE(Op::extract): r(d).u32 = (r(x).u32 >> immy) & r(z).u32; break;
+                    CASE(Op::pack):    r(d).u32 = r(x).u32 | (r(y).u32 << immz); break;
 
                     CASE(Op::bytes): {
                         const U32 table[] = {
@@ -1391,14 +1657,15 @@
                             (r(x).u32 >> 16) & 0xff,
                             (r(x).u32 >> 24) & 0xff,
                         };
-                        r(d).u32 = table[(imm >>  0) & 0xf] <<  0
-                                 | table[(imm >>  4) & 0xf] <<  8
-                                 | table[(imm >>  8) & 0xf] << 16
-                                 | table[(imm >> 12) & 0xf] << 24;
+                        r(d).u32 = table[(immy >>  0) & 0xf] <<  0
+                                 | table[(immy >>  4) & 0xf] <<  8
+                                 | table[(immy >>  8) & 0xf] << 16
+                                 | table[(immy >> 12) & 0xf] << 24;
                     } break;
 
                     CASE(Op::to_f32): r(d).f32 = skvx::cast<float>(r(x).i32); break;
-                    CASE(Op::to_i32): r(d).i32 = skvx::cast<int>  (r(x).f32); break;
+                    CASE(Op::trunc):  r(d).i32 = skvx::cast<int>  (r(x).f32); break;
+                    CASE(Op::round):  r(d).i32 = skvx::cast<int>  (r(x).f32 + 0.5f); break;
                 #undef CASE
                 }
             }
@@ -1456,7 +1723,7 @@
         , fOriginalProgram(instructions)
     {
         this->setupInterpreter(instructions);
-    #if defined(SKVM_JIT)
+    #if 1 && defined(SKVM_JIT)
         this->setupJIT(instructions, debug_name);
     #endif
     }
@@ -1498,12 +1765,15 @@
             if (inst.y != inst.x                    ) { maybe_recycle_register(inst.y); }
             if (inst.z != inst.x && inst.z != inst.y) { maybe_recycle_register(inst.z); }
 
-            // Allocate a register if we have to, preferring to reuse anything available.
-            if (avail.empty()) {
-                reg[id] = fRegs++;
-            } else {
-                reg[id] = avail.back();
-                avail.pop_back();
+            // Instructions that die at themselves (stores) don't need a register.
+            if (inst.death != id) {
+                // Allocate a register if we have to, preferring to reuse anything available.
+                if (avail.empty()) {
+                    reg[id] = fRegs++;
+                } else {
+                    reg[id] = avail.back();
+                    avail.pop_back();
+                }
             }
         };
 
@@ -1534,10 +1804,11 @@
                 inst.op,
                 lookup_register(id),
                 lookup_register(inst.x),
-                lookup_register(inst.y),
+               {lookup_register(inst.y)},
                {lookup_register(inst.z)},
             };
-            if (inst.z == NA) { pinst.imm = inst.imm; }
+            if (inst.y == NA) { pinst.immy = inst.immy; }
+            if (inst.z == NA) { pinst.immz = inst.immz; }
             fInstructions.push_back(pinst);
         };
 
@@ -1593,6 +1864,7 @@
 
     bool Program::jit(const std::vector<Builder::Instruction>& instructions,
                       const bool try_hoisting,
+                      std::vector<LineTableEntry>* line_table,
                       Assembler* a) const {
         using A = Assembler;
 
@@ -1611,16 +1883,18 @@
         if (!SkCpu::Supports(SkCpu::HSW)) {
             return false;
         }
-        A::GP64 N     = A::rdi,
-                arg[] = { A::rsi, A::rdx, A::rcx, A::r8, A::r9 };
+        A::GP64 N       = A::rdi,
+                scratch = A::rax,
+                arg[]   = { A::rsi, A::rdx, A::rcx, A::r8, A::r9 };
 
         // All 16 ymm registers are available to use.
         using Reg = A::Ymm;
         uint32_t avail = 0xffff;
 
     #elif defined(__aarch64__)
-        A::X N     = A::x0,
-             arg[] = { A::x1, A::x2, A::x3, A::x4, A::x5, A::x6, A::x7 };
+        A::X N       = A::x0,
+             scratch = A::x8,
+             arg[]   = { A::x1, A::x2, A::x3, A::x4, A::x5, A::x6, A::x7 };
 
         // We can use v0-v7 and v16-v31 freely; we'd need to preserve v8-v15.
         using Reg = A::V;
@@ -1639,28 +1913,29 @@
             A::Label label;
             Reg      reg;
         };
-        SkTHashMap<int, LabelAndReg> splats,
-                                     bytes_masks;
+        SkTHashMap<int, LabelAndReg> constants,    // All constants share the same pool.
+                                     bytes_masks;  // These vary per-lane.
+        LabelAndReg                  iota;         // Exists _only_ to vary per-lane.
+
+        auto mark_line = [&](int line) {
+            if (line_table) {
+                line_table->push_back({line, a->size()});
+            }
+        };
 
         auto warmup = [&](Val id) {
             const Builder::Instruction& inst = instructions[id];
 
-            Op op = inst.op;
-            int imm = inst.imm;
-
-            switch (op) {
+            switch (inst.op) {
                 default: break;
 
-                case Op::splat: if (!splats.find(imm)) { splats.set(imm, {}); }
-                                break;
-
-                case Op::bytes: if (!bytes_masks.find(imm)) {
-                                    bytes_masks.set(imm, {});
+                case Op::bytes: if (!bytes_masks.find(inst.immy)) {
+                                    bytes_masks.set(inst.immy, {});
                                     if (try_hoisting) {
                                         // vpshufb can always work with the mask from memory,
                                         // but it helps to hoist the mask to a register for tbl.
                                     #if defined(__aarch64__)
-                                        LabelAndReg* entry = bytes_masks.find(imm);
+                                        LabelAndReg* entry = bytes_masks.find(inst.immy);
                                         if (int found = __builtin_ffs(avail)) {
                                             entry->reg = (Reg)(found-1);
                                             avail ^= 1 << entry->reg;
@@ -1683,7 +1958,8 @@
             Val x = inst.x,
                 y = inst.y,
                 z = inst.z;
-            int imm = inst.imm;
+            int immy = inst.immy,
+                immz = inst.immz;
 
             // Most (but not all) ops create an output value and need a register to hold it, dst.
             // We track each instruction's dst in r[] so we can thread it through as an input
@@ -1782,62 +2058,72 @@
                     return false;  // TODO: many new ops
 
             #if defined(__x86_64__)
-                case Op::store8: if (scalar) { a->vpextrb  (arg[imm], (A::Xmm)r[x], 0); }
+                case Op::assert_true: {
+                    a->vptest (r[x], &constants[0xffffffff].label);
+                    A::Label all_true;
+                    a->jc(&all_true);
+                    a->int3();
+                    a->label(&all_true);
+                } break;
+
+                case Op::store8: if (scalar) { a->vpextrb  (arg[immy], (A::Xmm)r[x], 0); }
                                  else        { a->vpackusdw(tmp(), r[x], r[x]);
                                                a->vpermq   (tmp(), tmp(), 0xd8);
                                                a->vpackuswb(tmp(), tmp(), tmp());
-                                               a->vmovq    (arg[imm], (A::Xmm)tmp()); }
+                                               a->vmovq    (arg[immy], (A::Xmm)tmp()); }
                                                break;
 
-                case Op::store16: if (scalar) { a->vpextrw  (arg[imm], (A::Xmm)r[x], 0); }
+                case Op::store16: if (scalar) { a->vpextrw  (arg[immy], (A::Xmm)r[x], 0); }
                                   else        { a->vpackusdw(tmp(), r[x], r[x]);
                                                 a->vpermq   (tmp(), tmp(), 0xd8);
-                                                a->vmovups  (arg[imm], (A::Xmm)tmp()); }
+                                                a->vmovups  (arg[immy], (A::Xmm)tmp()); }
                                                 break;
 
-                case Op::store32: if (scalar) { a->vmovd  (arg[imm], (A::Xmm)r[x]); }
-                                  else        { a->vmovups(arg[imm],         r[x]); }
+                case Op::store32: if (scalar) { a->vmovd  (arg[immy], (A::Xmm)r[x]); }
+                                  else        { a->vmovups(arg[immy],         r[x]); }
                                                 break;
 
                 case Op::load8:  if (scalar) {
                                      a->vpxor  (dst(), dst(), dst());
-                                     a->vpinsrb((A::Xmm)dst(), (A::Xmm)dst(), arg[imm], 0);
+                                     a->vpinsrb((A::Xmm)dst(), (A::Xmm)dst(), arg[immy], 0);
                                  } else {
-                                     a->vpmovzxbd(dst(), arg[imm]);
+                                     a->vpmovzxbd(dst(), arg[immy]);
                                  } break;
 
                 case Op::load16: if (scalar) {
                                      a->vpxor  (dst(), dst(), dst());
-                                     a->vpinsrw((A::Xmm)dst(), (A::Xmm)dst(), arg[imm], 0);
+                                     a->vpinsrw((A::Xmm)dst(), (A::Xmm)dst(), arg[immy], 0);
                                  } else {
-                                     a->vpmovzxwd(dst(), arg[imm]);
+                                     a->vpmovzxwd(dst(), arg[immy]);
                                  } break;
 
-                case Op::load32: if (scalar) { a->vmovd  ((A::Xmm)dst(), arg[imm]); }
-                                 else        { a->vmovups(        dst(), arg[imm]); }
+                case Op::load32: if (scalar) { a->vmovd  ((A::Xmm)dst(), arg[immy]); }
+                                 else        { a->vmovups(        dst(), arg[immy]); }
                                  break;
 
-                case Op::uniform8: a->movzbl(A::rax, arg[imm&0xffff], imm>>16);
-                                   a->vmovd_direct((A::Xmm)dst(), A::rax);
+                case Op::uniform8: a->movzbl(scratch, arg[immy], immz);
+                                   a->vmovd_direct((A::Xmm)dst(), scratch);
                                    a->vbroadcastss(dst(), (A::Xmm)dst());
                                    break;
 
-                case Op::uniform32: a->vbroadcastss(dst(), arg[imm&0xffff], imm>>16);
+                case Op::uniform32: a->vbroadcastss(dst(), arg[immy], immz);
                                     break;
 
-                case Op::splat: a->vbroadcastss(dst(), &splats.find(imm)->label);
+                case Op::index: a->vmovd_direct((A::Xmm)tmp(), N);
+                                a->vbroadcastss(tmp(), (A::Xmm)tmp());
+                                a->vpsubd(dst(), tmp(), &iota.label);
                                 break;
-                                // TODO: many of these instructions have variants that
-                                // can read one of their arugments from 32-byte memory
-                                // instead of a register.  Find a way to avoid needing
-                                // to splat most* constants out at all?
-                                // (*Might work for x - 255 but not 255 - x, so will
-                                // always need to be able to splat to a register.)
+
+                case Op::splat: if (immy) { a->vbroadcastss(dst(), &constants[immy].label); }
+                                else      { a->vpxor(dst(), dst(), dst()); }
+                                break;
 
                 case Op::add_f32: a->vaddps(dst(), r[x], r[y]); break;
                 case Op::sub_f32: a->vsubps(dst(), r[x], r[y]); break;
                 case Op::mul_f32: a->vmulps(dst(), r[x], r[y]); break;
                 case Op::div_f32: a->vdivps(dst(), r[x], r[y]); break;
+                case Op::min_f32: a->vminps(dst(), r[x], r[y]); break;
+                case Op::max_f32: a->vmaxps(dst(), r[x], r[y]); break;
 
                 case Op::mad_f32:
                     if      (avail & (1<<r[x])) { set_dst(r[x]); a->vfmadd132ps(r[x], r[z], r[y]); }
@@ -1848,13 +2134,15 @@
                                                                  a->vfmadd132ps(dst(),r[z], r[y]); }
                                                                  break;
 
+                case Op::mul_f32_imm: a->vmulps(dst(), r[x], &constants[immy].label); break;
+
                 case Op::add_i32: a->vpaddd (dst(), r[x], r[y]); break;
                 case Op::sub_i32: a->vpsubd (dst(), r[x], r[y]); break;
                 case Op::mul_i32: a->vpmulld(dst(), r[x], r[y]); break;
 
                 case Op::sub_i16x2: a->vpsubw (dst(), r[x], r[y]); break;
                 case Op::mul_i16x2: a->vpmullw(dst(), r[x], r[y]); break;
-                case Op::shr_i16x2: a->vpsrlw (dst(), r[x],  imm); break;
+                case Op::shr_i16x2: a->vpsrlw (dst(), r[x], immy); break;
 
                 case Op::bit_and  : a->vpand (dst(), r[x], r[y]); break;
                 case Op::bit_or   : a->vpor  (dst(), r[x], r[y]); break;
@@ -1862,52 +2150,67 @@
                 case Op::bit_clear: a->vpandn(dst(), r[y], r[x]); break;  // N.B. Y then X.
                 case Op::select   : a->vpblendvb(dst(), r[z], r[y], r[x]); break;
 
-                case Op::shl_i32: a->vpslld(dst(), r[x], imm); break;
-                case Op::shr_i32: a->vpsrld(dst(), r[x], imm); break;
-                case Op::sra_i32: a->vpsrad(dst(), r[x], imm); break;
+                case Op::shl_i32: a->vpslld(dst(), r[x], immy); break;
+                case Op::shr_i32: a->vpsrld(dst(), r[x], immy); break;
+                case Op::sra_i32: a->vpsrad(dst(), r[x], immy); break;
 
                 case Op::eq_i32: a->vpcmpeqd(dst(), r[x], r[y]); break;
-                case Op::lt_i32: a->vpcmpgtd(dst(), r[y], r[x]); break;
                 case Op::gt_i32: a->vpcmpgtd(dst(), r[x], r[y]); break;
 
-                case Op::extract: if (imm == 0) { a->vpand (dst(),  r[x], r[y]); }
-                                  else          { a->vpsrld(tmp(),  r[x], imm);
-                                                  a->vpand (dst(), tmp(), r[y]); }
+                case Op:: eq_f32: a->vcmpeqps (dst(), r[x], r[y]); break;
+                case Op::neq_f32: a->vcmpneqps(dst(), r[x], r[y]); break;
+                case Op:: gt_f32: a->vcmpltps (dst(), r[y], r[x]); break;
+                case Op::gte_f32: a->vcmpleps (dst(), r[y], r[x]); break;
+
+                case Op::extract: if (immy == 0) { a->vpand (dst(),  r[x], r[z]); }
+                                  else           { a->vpsrld(tmp(),  r[x], immy);
+                                                   a->vpand (dst(), tmp(), r[z]); }
                                   break;
 
-                case Op::pack: a->vpslld(tmp(),  r[y], imm);
+                case Op::pack: a->vpslld(tmp(),  r[y], immz);
                                a->vpor  (dst(), tmp(), r[x]);
                                break;
 
                 case Op::to_f32: a->vcvtdq2ps (dst(), r[x]); break;
-                case Op::to_i32: a->vcvttps2dq(dst(), r[x]); break;
+                case Op::trunc : a->vcvttps2dq(dst(), r[x]); break;
+                case Op::round : a->vcvtps2dq (dst(), r[x]); break;
 
-                case Op::bytes: a->vpshufb(dst(), r[x], &bytes_masks.find(imm)->label);
+                case Op::bytes: a->vpshufb(dst(), r[x], &bytes_masks.find(immy)->label);
                                 break;
 
             #elif defined(__aarch64__)
+                case Op::assert_true: {
+                    a->uminv4s(tmp(), r[x]);   // uminv acts like an all() across the vector.
+                    a->fmovs(scratch, tmp());
+                    A::Label all_true;
+                    a->cbnz(scratch, &all_true);
+                    a->brk(0);
+                    a->label(&all_true);
+                } break;
+
                 case Op::store8: a->xtns2h(tmp(), r[x]);
                                  a->xtnh2b(tmp(), tmp());
-                   if (scalar) { a->strb  (tmp(), arg[imm]); }
-                   else        { a->strs  (tmp(), arg[imm]); }
+                   if (scalar) { a->strb  (tmp(), arg[immy]); }
+                   else        { a->strs  (tmp(), arg[immy]); }
                                  break;
                 // TODO: another case where it'd be okay to alias r[x] and tmp if r[x] dies here.
 
-                case Op::store32: if (scalar) { a->strs(r[x], arg[imm]); }
-                                  else        { a->strq(r[x], arg[imm]); }
+                case Op::store32: if (scalar) { a->strs(r[x], arg[immy]); }
+                                  else        { a->strq(r[x], arg[immy]); }
                                                 break;
 
-                case Op::load8: if (scalar) { a->ldrb(tmp(), arg[imm]); }
-                                else        { a->ldrs(tmp(), arg[imm]); }
+                case Op::load8: if (scalar) { a->ldrb(tmp(), arg[immy]); }
+                                else        { a->ldrs(tmp(), arg[immy]); }
                                               a->uxtlb2h(tmp(), tmp());
                                               a->uxtlh2s(dst(), tmp());
                                               break;
 
-                case Op::load32: if (scalar) { a->ldrs(dst(), arg[imm]); }
-                                 else        { a->ldrq(dst(), arg[imm]); }
+                case Op::load32: if (scalar) { a->ldrs(dst(), arg[immy]); }
+                                 else        { a->ldrq(dst(), arg[immy]); }
                                                break;
 
-                case Op::splat: a->ldrq(dst(), &splats.find(imm)->label);
+                case Op::splat: if (immy) { a->ldrq(dst(), &constants[immy].label); }
+                                else      { a->eor16b(dst(), dst(), dst()); }
                                 break;
                                 // TODO: If we hoist these, pack 4 values in each register
                                 // and use vector/lane operations, cutting the register
@@ -1917,6 +2220,8 @@
                 case Op::sub_f32: a->fsub4s(dst(), r[x], r[y]); break;
                 case Op::mul_f32: a->fmul4s(dst(), r[x], r[y]); break;
                 case Op::div_f32: a->fdiv4s(dst(), r[x], r[y]); break;
+                case Op::min_f32: a->fmin4s(dst(), r[x], r[y]); break;
+                case Op::max_f32: a->fmax4s(dst(), r[x], r[y]); break;
 
                 case Op::mad_f32: // fmla4s is z += x*y
                     if (avail & (1<<r[z])) { set_dst(r[z]); a->fmla4s( r[z],  r[x],  r[y]);   }
@@ -1925,6 +2230,15 @@
                                        if(dst() != tmp()) { a->orr16b(dst(), tmp(), tmp()); } }
                                                             break;
 
+                // We should not see _imm ops on ARM.
+                case Op::mul_f32_imm: SkUNREACHABLE; break;
+
+                case Op:: gt_f32: a->fcmgt4s (dst(), r[x], r[y]); break;
+                case Op::gte_f32: a->fcmge4s (dst(), r[x], r[y]); break;
+                case Op:: eq_f32: a->fcmeq4s (dst(), r[x], r[y]); break;
+                case Op::neq_f32: a->fcmeq4s (tmp(), r[x], r[y]);
+                                  a->not16b  (dst(), tmp());      break;
+
 
                 case Op::add_i32: a->add4s(dst(), r[x], r[y]); break;
                 case Op::sub_i32: a->sub4s(dst(), r[x], r[y]); break;
@@ -1932,7 +2246,7 @@
 
                 case Op::sub_i16x2: a->sub8h (dst(), r[x], r[y]); break;
                 case Op::mul_i16x2: a->mul8h (dst(), r[x], r[y]); break;
-                case Op::shr_i16x2: a->ushr8h(dst(), r[x],  imm); break;
+                case Op::shr_i16x2: a->ushr8h(dst(), r[x], immy); break;
 
                 case Op::bit_and  : a->and16b(dst(), r[x], r[y]); break;
                 case Op::bit_or   : a->orr16b(dst(), r[x], r[y]); break;
@@ -1946,36 +2260,39 @@
                                        if(dst() != tmp()) { a->orr16b(dst(), tmp(), tmp()); } }
                                                             break;
 
-                case Op::shl_i32: a-> shl4s(dst(), r[x], imm); break;
-                case Op::shr_i32: a->ushr4s(dst(), r[x], imm); break;
-                case Op::sra_i32: a->sshr4s(dst(), r[x], imm); break;
+                case Op::shl_i32: a-> shl4s(dst(), r[x], immy); break;
+                case Op::shr_i32: a->ushr4s(dst(), r[x], immy); break;
+                case Op::sra_i32: a->sshr4s(dst(), r[x], immy); break;
 
                 case Op::eq_i32: a->cmeq4s(dst(), r[x], r[y]); break;
-                case Op::lt_i32: a->cmgt4s(dst(), r[y], r[x]); break;
                 case Op::gt_i32: a->cmgt4s(dst(), r[x], r[y]); break;
 
-                case Op::extract: if (imm) { a->ushr4s(tmp(), r[x], imm);
-                                             a->and16b(dst(), tmp(), r[y]); }
-                                  else     { a->and16b(dst(), r[x], r[y]); }
-                                             break;
+                case Op::extract: if (immy) { a->ushr4s(tmp(),  r[x], immy);
+                                              a->and16b(dst(), tmp(), r[z]); }
+                                  else      { a->and16b(dst(),  r[x], r[z]); }
+                                              break;
 
                 case Op::pack:
-                    if (avail & (1<<r[x])) { set_dst(r[x]); a->sli4s ( r[x],  r[y],  imm); }
-                    else                   {                a->shl4s (tmp(),  r[y],  imm);
-                                                            a->orr16b(dst(), tmp(), r[x]); }
+                    if (avail & (1<<r[x])) { set_dst(r[x]); a->sli4s ( r[x],  r[y],  immz); }
+                    else                   {                a->shl4s (tmp(),  r[y],  immz);
+                                                            a->orr16b(dst(), tmp(),  r[x]); }
                                                             break;
 
                 case Op::to_f32: a->scvtf4s (dst(), r[x]); break;
-                case Op::to_i32: a->fcvtzs4s(dst(), r[x]); break;
+                case Op::trunc:  a->fcvtzs4s(dst(), r[x]); break;
+                case Op::round:  a->fcvtns4s(dst(), r[x]); break;
 
                 case Op::bytes:
-                    if (try_hoisting) { a->tbl (dst(), r[x], bytes_masks.find(imm)->reg); }
-                    else              { a->ldrq(tmp(), &bytes_masks.find(imm)->label);
+                    if (try_hoisting) { a->tbl (dst(), r[x], bytes_masks.find(immy)->reg); }
+                    else              { a->ldrq(tmp(), &bytes_masks.find(immy)->label);
                                         a->tbl (dst(), r[x], tmp()); }
                                         break;
             #endif
             }
 
+            // Leave plenty of room for loop overhead and constant "line" markers.
+            mark_line(id+1000);
+
             // Calls to tmp() or dst() might have flipped this false from its default true state.
             return ok;
         };
@@ -2014,10 +2331,12 @@
             }
         }
 
+        int line = 1;  // All loop overhead is marked as "line 1".
         a->label(&body);
         {
             a->cmp(N, K);
             jump_if_less(&tail);
+            mark_line(line);
             for (Val id = 0; id < (Val)instructions.size(); id++) {
                 if (!hoisted(id) && !emit(id, /*scalar=*/false)) {
                     return false;
@@ -2030,12 +2349,14 @@
             }
             sub(N, K);
             jump(&body);
+            mark_line(line);
         }
 
         a->label(&tail);
         {
             a->cmp(N, 1);
             jump_if_less(&done);
+            mark_line(line);
             for (Val id = 0; id < (Val)instructions.size(); id++) {
                 if (!hoisted(id) && !emit(id, /*scalar=*/true)) {
                     return false;
@@ -2048,21 +2369,32 @@
             }
             sub(N, 1);
             jump(&tail);
+            mark_line(line);
         }
 
         a->label(&done);
         {
             exit();
+            mark_line(line);
         }
 
+        // Except for explicit aligned load and store instructions, AVX allows
+        // memory operands to be unaligned.  So even though we're creating 16
+        // byte patterns on ARM or 32-byte patterns on x86, we only need to
+        // align to 4 bytes, the element size and alignment requirement.
+
+        constants.foreach([&](int imm, LabelAndReg* entry) {
+            a->align(4);
+            a->label(&entry->label);
+            for (int i = 0; i < K; i++) {
+                a->word(imm);
+            }
+            mark_line(++line);   // 2,3,4...
+        });
+
         bytes_masks.foreach([&](int imm, LabelAndReg* entry) {
             // One 16-byte pattern for ARM tbl, that same pattern twice for x86-64 vpshufb.
-        #if defined(__x86_64__)
-            a->align(32);
-        #elif defined(__aarch64__)
             a->align(4);
-        #endif
-
             a->label(&entry->label);
             int mask[4];
             bytes_control(imm, mask);
@@ -2070,19 +2402,17 @@
         #if defined(__x86_64__)
             a->bytes(mask, sizeof(mask));
         #endif
+            mark_line(++line);
         });
 
-        splats.foreach([&](int imm, LabelAndReg* entry) {
-            // vbroadcastss 4 bytes on x86-64, or simply load 16-bytes on aarch64.
+        if (!iota.label.references.empty()) {
             a->align(4);
-            a->label(&entry->label);
-            a->word(imm);
-        #if defined(__aarch64__)
-            a->word(imm);
-            a->word(imm);
-            a->word(imm);
-        #endif
-        });
+            a->label(&iota.label);
+            for (int i = 0; i < K; i++) {
+                a->word(i);
+            }
+            mark_line(++line);
+        }
 
         return true;
     }
@@ -2095,9 +2425,9 @@
         // First try allowing code hoisting (faster code)
         // then again without if that fails (lower register pressure).
         bool try_hoisting = true;
-        if (!this->jit(instructions, try_hoisting, &a)) {
+        if (!this->jit(instructions, try_hoisting, nullptr, &a)) {
             try_hoisting = false;
-            if (!this->jit(instructions, try_hoisting, &a)) {
+            if (!this->jit(instructions, try_hoisting, nullptr, &a)) {
                 return;
             }
         }
@@ -2108,14 +2438,18 @@
         fJITBuf = mmap(nullptr,fJITSize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1,0);
 
         // Assemble the program for real.
+        std::vector<LineTableEntry> line_table;
         a = Assembler{fJITBuf};
-        SkAssertResult(this->jit(instructions, try_hoisting, &a));
+        SkAssertResult(this->jit(instructions, try_hoisting, &line_table, &a));
         SkASSERT(a.size() <= fJITSize);
 
         // Remap as executable, and flush caches on platforms that need that.
         mprotect(fJITBuf, fJITSize, PROT_READ|PROT_EXEC);
         __builtin___clear_cache((char*)fJITBuf,
                                 (char*)fJITBuf + fJITSize);
+
+        // Hook into profilers and such for debugging and development.
+        notify_vtune(debug_name, fJITBuf, a.size(), line_table);
     #if defined(SKVM_PERF_DUMPS)
         this->dumpJIT(debug_name, a.size());
     #endif
@@ -2124,10 +2458,9 @@
 
 #if defined(SKVM_PERF_DUMPS)
     void Program::dumpJIT(const char* debug_name, size_t size) const {
+        SkASSERT(debug_name);
     #if 0 && defined(__aarch64__)
-        if (debug_name) {
-            SkDebugf("\n%s:", debug_name);
-        }
+        SkDebugf("\n%s:", debug_name);
         // cat | llvm-mc -arch aarch64 -disassemble
         auto cur = (const uint8_t*)fJITBuf;
         for (int i = 0; i < (int)size; i++) {
@@ -2143,24 +2476,6 @@
         static SkSpinlock dump_lock;
         SkAutoSpinlock lock(dump_lock);
 
-        auto fnv1a = [](const void* vbuf, size_t n) {
-            uint32_t hash = 2166136261;
-            for (auto buf = (const uint8_t*)vbuf; n --> 0; buf++) {
-                hash ^= *buf;
-                hash *= 16777619;
-            }
-            return hash;
-        };
-
-
-        char name[64];
-        uint32_t hash = fnv1a(fJITBuf, size);
-        if (debug_name) {
-            sprintf(name, "skvm-jit-%s", debug_name);
-        } else {
-            sprintf(name, "skvm-jit-%u", hash);
-        }
-
         // Create a jit-<pid>.dump file that we can `perf inject -j` into a
         // perf.data captured with `perf record -k 1`, letting us see each
         // JIT'd Program as if a function named skvm-jit-<hash>.   E.g.
@@ -2224,6 +2539,8 @@
             return f;
         }();
 
+        static uint64_t next_id = 1;
+
         struct CodeLoad {
             uint32_t event_type, event_size;
             uint64_t timestamp_ns;
@@ -2231,16 +2548,16 @@
             uint32_t pid, tid;
             uint64_t vma/*???*/, code_addr, code_size, id;
         } load = {
-            0/*code load*/, (uint32_t)(sizeof(CodeLoad) + strlen(name) + 1 + size),
+            0/*code load*/, (uint32_t)(sizeof(CodeLoad) + strlen(debug_name) + 1 + size),
             timestamp_ns(),
 
             (uint32_t)getpid(), (uint32_t)SkGetThreadID(),
-            (uint64_t)fJITBuf, (uint64_t)fJITBuf, size, hash,
+            (uint64_t)fJITBuf, (uint64_t)fJITBuf, size, next_id++,
         };
 
         // Write the header, the JIT'd function name, and the JIT'd code itself.
         fwrite(&load, sizeof(load), 1, jitdump);
-        fwrite(name, 1, strlen(name), jitdump);
+        fwrite(debug_name, 1, strlen(debug_name), jitdump);
         fwrite("\0", 1, 1, jitdump);
         fwrite(fJITBuf, 1, size, jitdump);
     }
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index 06b189a..779d724 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -10,7 +10,6 @@
 
 #include "include/core/SkTypes.h"
 #include "include/private/SkTHash.h"
-#include <functional>  // std::hash
 #include <vector>      // std::vector
 
 class SkWStream;
@@ -59,6 +58,7 @@
 
         void align(int mod);
 
+        void int3();
         void vzeroupper();
         void ret();
 
@@ -70,25 +70,32 @@
         DstEqXOpY vpand, vpor, vpxor, vpandn,
                   vpaddd, vpsubd, vpmulld,
                           vpsubw, vpmullw,
-                  vaddps, vsubps, vmulps, vdivps,
+                  vaddps, vsubps, vmulps, vdivps, vminps, vmaxps,
                   vfmadd132ps, vfmadd213ps, vfmadd231ps,
                   vpackusdw, vpackuswb,
                   vpcmpeqd, vpcmpgtd;
 
+        // Floating point comparisons are all the same instruction with varying imm.
+        void vcmpps(Ymm dst, Ymm x, Ymm y, int imm);
+        void vcmpeqps (Ymm dst, Ymm x, Ymm y) { this->vcmpps(dst,x,y,0); }
+        void vcmpltps (Ymm dst, Ymm x, Ymm y) { this->vcmpps(dst,x,y,1); }
+        void vcmpleps (Ymm dst, Ymm x, Ymm y) { this->vcmpps(dst,x,y,2); }
+        void vcmpneqps(Ymm dst, Ymm x, Ymm y) { this->vcmpps(dst,x,y,4); }
+
         using DstEqXOpImm = void(Ymm dst, Ymm x, int imm);
         DstEqXOpImm vpslld, vpsrld, vpsrad,
                     vpsrlw,
                     vpermq;
 
         using DstEqOpX = void(Ymm dst, Ymm x);
-        DstEqOpX vmovdqa, vcvtdq2ps, vcvttps2dq;
+        DstEqOpX vmovdqa, vcvtdq2ps, vcvttps2dq, vcvtps2dq;
 
         void vpblendvb(Ymm dst, Ymm x, Ymm y, Ymm z);
 
         struct Label {
-            int                                 offset = 0;
-            enum { None, ARMDisp19, X86Disp32 } kind = None;
-            std::vector<int>                    references;
+            int                                      offset = 0;
+            enum { NotYetSet, ARMDisp19, X86Disp32 } kind = NotYetSet;
+            std::vector<int>                         references;
         };
 
         Label here();
@@ -98,13 +105,19 @@
         void je (Label*);
         void jne(Label*);
         void jl (Label*);
+        void jc (Label*);
         void cmp(GP64, int imm);
 
+        void vptest(Ymm dst, Label*);
+
         void vbroadcastss(Ymm dst, Label*);
         void vbroadcastss(Ymm dst, Xmm src);
         void vbroadcastss(Ymm dst, GP64 ptr, int off);  // dst = *(ptr+off)
 
         void vpshufb(Ymm dst, Ymm x, Label*);
+        void vpaddd (Ymm dst, Ymm x, Label*);
+        void vpsubd (Ymm dst, Ymm x, Label*);
+        void vmulps (Ymm dst, Ymm x, Label*);
 
         void vmovups  (Ymm dst, GP64 ptr);   // dst = *ptr, 256-bit
         void vpmovzxwd(Ymm dst, GP64 ptr);   // dst = *ptr, 128-bit, each uint16_t expanded to int
@@ -136,9 +149,13 @@
                add4s,  sub4s,  mul4s,
               cmeq4s, cmgt4s,
                        sub8h,  mul8h,
-              fadd4s, fsub4s, fmul4s, fdiv4s,
+              fadd4s, fsub4s, fmul4s, fdiv4s, fmin4s, fmax4s,
+              fcmeq4s, fcmgt4s, fcmge4s,
               tbl;
 
+        // TODO: there are also float ==,<,<=,>,>= instructions with an immediate 0.0f,
+        // and the register comparison > and >= can also compare absolute values.  Interesting.
+
         // d += n*m
         void fmla4s(V d, V n, V m);
 
@@ -150,15 +167,17 @@
 
         // d = op(n)
         using DOpN = void(V d, V n);
-        DOpN scvtf4s,   // int -> float
+        DOpN not16b,    // d = ~n
+             scvtf4s,   // int -> float
              fcvtzs4s,  // truncate float -> int
+             fcvtns4s,  // round float -> int
              xtns2h,    // u32 -> u16
              xtnh2b,    // u16 -> u8
              uxtlb2h,   // u8 -> u16
-             uxtlh2s;   // u16 -> u32
+             uxtlh2s,   // u16 -> u32
+             uminv4s;   // dst[0] = min(n[0],n[1],n[2],n[3]), n as unsigned
 
-        // TODO: both these platforms support rounding float->int (vcvtps2dq, fcvtns.4s)... use?
-
+        void brk (int imm16);
         void ret (X);
         void add (X d, X n, int imm12);
         void sub (X d, X n, int imm12);
@@ -190,6 +209,8 @@
         void strs(V src, X dst);  //  32-bit *dst = src
         void strb(V src, X dst);  //   8-bit *dst = src
 
+        void fmovs(X dst, V src); // dst = 32-bit src[0]
+
     private:
         // dst = op(dst, imm)
         void op(int opcode, int opcode_ext, GP64 dst, int imm);
@@ -235,9 +256,10 @@
     };
 
     enum class Op : uint8_t {
+          assert_true,
           store8,   store16,   store32,
     // ↑ side effects / no side effects ↓
-
+           index,
            load8,    load16,    load32,
          gather8,  gather16,  gather32,
     // ↑ always varying / uniforms, constants, Just Math ↓
@@ -249,17 +271,18 @@
         sub_f32, sub_i32, sub_i16x2,
         mul_f32, mul_i32, mul_i16x2,
         div_f32,
+        min_f32,
+        max_f32,
         mad_f32,
                  shl_i32, shl_i16x2,
                  shr_i32, shr_i16x2,
                  sra_i32, sra_i16x2,
+        mul_f32_imm,
 
-         to_i32,  to_f32,
+         trunc, round,  to_f32,
 
          eq_f32,  eq_i32,  eq_i16x2,
         neq_f32, neq_i32, neq_i16x2,
-         lt_f32,  lt_i32,  lt_i16x2,
-        lte_f32, lte_i32, lte_i16x2,
          gt_f32,  gt_i32,  gt_i16x2,
         gte_f32, gte_i32, gte_i16x2,
 
@@ -287,7 +310,7 @@
         struct Instruction {
             Op  op;         // v* = op(x,y,z,imm), where * == index of this Instruction.
             Val x,y,z;      // Enough arguments for mad().
-            int imm;        // Immediate bit pattern, shift count, argument index, etc.
+            int immy,immz;  // Immediate bit pattern, shift count, argument index, etc.
 
             // Not populated until done() has been called.
             int  death;         // Index of last live instruction taking this input; live if != 0.
@@ -314,11 +337,16 @@
         // TODO: sign extension (signed types) for <32-bit loads?
         // TODO: unsigned integer operations where relevant (just comparisons?)?
 
+        void assert_true(I32 val);
+
         // Store {8,16,32}-bit varying.
         void store8 (Arg ptr, I32 val);
         void store16(Arg ptr, I32 val);
         void store32(Arg ptr, I32 val);
 
+        // Returns varying {n, n-1, n-2, ..., 1}, where n is the argument to Program::eval().
+        I32 index();
+
         // Load u8,u16,i32 varying.
         I32 load8 (Arg ptr);
         I32 load16(Arg ptr);
@@ -333,6 +361,16 @@
         I32 uniform8 (Arg ptr, int offset=0);
         I32 uniform16(Arg ptr, int offset=0);
         I32 uniform32(Arg ptr, int offset=0);
+        F32 uniformF (Arg ptr, int offset=0) { return this->bit_cast(this->uniform32(ptr,offset)); }
+
+        struct Uniform {
+            Arg ptr;
+            int offset;
+        };
+        I32 uniform8 (Uniform u) { return this->uniform8 (u.ptr, u.offset); }
+        I32 uniform16(Uniform u) { return this->uniform16(u.ptr, u.offset); }
+        I32 uniform32(Uniform u) { return this->uniform32(u.ptr, u.offset); }
+        F32 uniformF (Uniform u) { return this->uniformF (u.ptr, u.offset); }
 
         // Load an immediate constant.
         I32 splat(int      n);
@@ -344,6 +382,8 @@
         F32 sub(F32 x, F32 y);
         F32 mul(F32 x, F32 y);
         F32 div(F32 x, F32 y);
+        F32 min(F32 x, F32 y);
+        F32 max(F32 x, F32 y);
         F32 mad(F32 x, F32 y, F32 z);  //  x*y+z, often an FMA
 
         I32 eq (F32 x, F32 y);
@@ -353,7 +393,8 @@
         I32 gt (F32 x, F32 y);
         I32 gte(F32 x, F32 y);
 
-        I32 to_i32(F32 x);
+        I32 trunc(F32 x);
+        I32 round(F32 x);
         I32 bit_cast(F32 x) { return {x.id}; }
 
         // int math, comparisons, etc.
@@ -426,36 +467,69 @@
         //    - bytes(x, 0x0404) transforms an RGBA pixel into an A0A0 bit pattern.
         I32 bytes  (I32 x, int control);
 
-        I32 extract(I32 x, int bits, I32 y);   // (x >> bits) & y
+        I32 extract(I32 x, int bits, I32 z);   // (x >> bits) & z
         I32 pack   (I32 x, I32 y, int bits);   // x | (y << bits), assuming (x & (y << bits)) == 0
 
         void dump(SkWStream* = nullptr) const;
 
+        uint32_t hash() const;
+
     private:
         struct InstructionHash {
-            template <typename T>
-            static size_t Hash(T val) {
-                return std::hash<T>{}(val);
-            }
             size_t operator()(const Instruction& inst) const;
         };
 
-        Val push(Op, Val x, Val y=NA, Val z=NA, int imm=0);
-        bool isZero(Val) const;
+        Val push(Op, Val x, Val y=NA, Val z=NA, int immy=0, int immz=0);
+
+        bool allImm() const;
+
+        template <typename T, typename... Rest>
+        bool allImm(Val, T* imm, Rest...) const;
+
+        template <typename T>
+        bool isImm(Val id, T want) const {
+            T imm = 0;
+            return this->allImm(id, &imm) && imm == want;
+        }
 
         SkTHashMap<Instruction, Val, InstructionHash> fIndex;
         std::vector<Instruction>                      fProgram;
         std::vector<int>                              fStrides;
+        uint32_t                                      fHash{0};
     };
 
+    // Helper to streamline allocating and working with uniforms.
+    struct Uniforms {
+        Arg              ptr;
+        std::vector<int> buf;
+
+        explicit Uniforms(int init) : ptr(Arg{0}), buf(init) {}
+
+        Builder::Uniform push(const int* vals, int n) {
+            int offset = sizeof(int)*buf.size();
+            buf.insert(buf.end(), vals, vals+n);
+            return {ptr, offset};
+        }
+        Builder::Uniform pushF(const float* vals, int n) {
+            return this->push((const int*)vals, n);
+        }
+
+        Builder::Uniform push (int   val) { return this->push (&val, 1); }
+        Builder::Uniform pushF(float val) { return this->pushF(&val, 1); }
+    };
+
+    // Maps Builder::Instructions to regions of JIT'd code, for debugging in VTune.
+    struct LineTableEntry { int line; size_t offset; };
+
     using Reg = int;
 
     class Program {
     public:
-        struct Instruction {   // d = op(x, y, z/imm)
+        struct Instruction {   // d = op(x, y/imm, z/imm)
             Op  op;
-            Reg d,x,y;
-            union { Reg z; int imm; };
+            Reg d,x;
+            union { Reg y; int immy; };
+            union { Reg z; int immz; };
         };
 
         Program(const std::vector<Builder::Instruction>& instructions,
@@ -495,6 +569,7 @@
 
         bool jit(const std::vector<Builder::Instruction>&,
                  bool try_hoisting,
+                 std::vector<LineTableEntry>*,
                  Assembler*) const;
 
         // Dump jit-*.dump files for perf inject.
diff --git a/src/core/SkVMBlitter.cpp b/src/core/SkVMBlitter.cpp
index 5b767af..7c8ecb4 100644
--- a/src/core/SkVMBlitter.cpp
+++ b/src/core/SkVMBlitter.cpp
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "include/private/SkImageInfoPriv.h"
 #include "include/private/SkMacros.h"
 #include "src/core/SkArenaAlloc.h"
 #include "src/core/SkBlendModePriv.h"
@@ -13,45 +14,70 @@
 #include "src/core/SkCoreBlitters.h"
 #include "src/core/SkLRUCache.h"
 #include "src/core/SkVM.h"
+#include "src/core/SkVMBlitter.h"
 
 namespace {
 
+    // Uniforms set by the Blitter itself,
+    // rather than by the Shader, which follow this struct in the skvm::Uniforms buffer.
+    struct BlitterUniforms {
+        int right;  // First device x + blit run length n, used to get device x coordiate.
+        int y;      // Device y coordiate.
+    };
+    static_assert(SkIsAlign4(sizeof(BlitterUniforms)), "");
+    static constexpr int kBlitterUniformsCount = sizeof(BlitterUniforms) / 4;
+
     enum class Coverage { Full, UniformA8, MaskA8, MaskLCD16, Mask3D };
 
+    struct Params {
+        sk_sp<SkColorSpace> colorSpace;
+        sk_sp<SkShader>     shader;
+        SkColorType         colorType;
+        SkAlphaType         alphaType;
+        SkBlendMode         blendMode;
+        Coverage            coverage;
+
+        Params withCoverage(Coverage c) const {
+            Params p = *this;
+            p.coverage = c;
+            return p;
+        }
+    };
+
     SK_BEGIN_REQUIRE_DENSE;
     struct Key {
-        SkColorType    colorType;
-        SkAlphaType    alphaType;
-        Coverage       coverage;
-        SkBlendMode    blendMode;
-        SkShader*      shader;
-        SkColorFilter* colorFilter;
+        uint64_t colorSpace;
+        uint32_t shader;
+        uint8_t  colorType,
+                 alphaType,
+                 blendMode,
+                 coverage;
+
+        bool operator==(const Key& that) const {
+            return this->colorSpace == that.colorSpace
+                && this->shader     == that.shader
+                && this->colorType  == that.colorType
+                && this->alphaType  == that.alphaType
+                && this->blendMode  == that.blendMode
+                && this->coverage   == that.coverage;
+        }
 
         Key withCoverage(Coverage c) const {
             Key k = *this;
-            k.coverage = c;
+            k.coverage = SkToU8(c);
             return k;
         }
     };
     SK_END_REQUIRE_DENSE;
 
-    static bool operator==(const Key& x, const Key& y) {
-        return x.colorType   == y.colorType
-            && x.alphaType   == y.alphaType
-            && x.coverage    == y.coverage
-            && x.blendMode   == y.blendMode
-            && x.shader      == y.shader
-            && x.colorFilter == y.colorFilter;
-    }
-
     static SkString debug_name(const Key& key) {
-        return SkStringPrintf("CT%d-AT%d-Cov%d-Blend%d-Shader%d-CF%d",
+        return SkStringPrintf("CT%d-AT%d-Cov%d-Blend%d-CS%llx-Shader%x",
                               key.colorType,
                               key.alphaType,
                               key.coverage,
                               key.blendMode,
-                              SkToBool(key.shader),
-                              SkToBool(key.colorFilter));
+                              key.colorSpace,
+                              key.shader);
     }
 
     static bool debug_dump(const Key& key) {
@@ -64,7 +90,7 @@
     }
 
     static SkLRUCache<Key, skvm::Program>* try_acquire_program_cache() {
-    #if defined(SK_BUILD_FOR_IOS)
+    #if 0 || defined(SK_BUILD_FOR_IOS)
         // iOS doesn't support thread_local on versions less than 9.0. pthread
         // based fallbacks must be used there. We could also use an SkSpinlock
         // and tryAcquire()/release(), or...
@@ -78,128 +104,154 @@
     static void release_program_cache() { }
 
 
-    struct Uniforms {
-        uint32_t paint_color;
-        uint8_t  coverage;   // Used when Coverage::UniformA8.
-    };
-
     struct Builder : public skvm::Builder {
-        //using namespace skvm;
 
-        struct Color { skvm::I32 r,g,b,a; };
-
-
-        skvm::I32 inv(skvm::I32 x) {
-            return sub(splat(255), x);
+        skvm::F32 unorm(int bits, skvm::I32 x) {
+            float limit = (1<<bits)-1.0f;
+            return mul(to_f32(x), splat(1/limit));
+        }
+        skvm::I32 unorm(int bits, skvm::F32 x) {
+            float limit = (1<<bits)-1.0f;
+            return round(mul(x, splat(limit)));
         }
 
-        // TODO: provide this in skvm::Builder, with a custom NEON impl.
-        skvm::I32 div255(skvm::I32 v) {
-            // This should be a bit-perfect version of (v+127)/255,
-            // implemented as (v + ((v+128)>>8) + 128)>>8.
-            skvm::I32 v128 = add(v, splat(128));
-            return shr(add(v128, shr(v128, 8)), 8);
-        }
 
-        skvm::I32 mix(skvm::I32 x, skvm::I32 y, skvm::I32 t) {
-            return div255(add(mul(x, inv(t)),
-                              mul(y,     t )));
-        }
-
-        Color unpack_8888(skvm::I32 rgba) {
+        skvm::Color unpack_8888(skvm::I32 rgba) {
             return {
-                extract(rgba,  0, splat(0xff)),
-                extract(rgba,  8, splat(0xff)),
-                extract(rgba, 16, splat(0xff)),
-                extract(rgba, 24, splat(0xff)),
+                unorm(8, extract(rgba,  0, splat(0xff))),
+                unorm(8, extract(rgba,  8, splat(0xff))),
+                unorm(8, extract(rgba, 16, splat(0xff))),
+                unorm(8, extract(rgba, 24, splat(0xff))),
             };
         }
 
-        skvm::I32 pack_8888(Color c) {
-            return pack(pack(c.r, c.g, 8),
-                        pack(c.b, c.a, 8), 16);
-        }
-
-        Color unpack_565(skvm::I32 bgr) {
-            // N.B. kRGB_565_SkColorType is named confusingly;
-            //      blue is in the low bits and red the high.
-            skvm::I32 r = extract(bgr, 11, splat(0b011'111)),
-                      g = extract(bgr,  5, splat(0b111'111)),
-                      b = extract(bgr,  0, splat(0b011'111));
+        skvm::Color unpack_565(skvm::I32 bgr) {
             return {
-                // Scale 565 up to 888.
-                bit_or(shl(r, 3), shr(r, 2)),
-                bit_or(shl(g, 2), shr(g, 4)),
-                bit_or(shl(b, 3), shr(b, 2)),
-                splat(0xff),
+                unorm(5, extract(bgr, 11, splat(0b011'111))),
+                unorm(6, extract(bgr,  5, splat(0b111'111))),
+                unorm(5, extract(bgr,  0, splat(0b011'111))),
+                splat(1.0f),
             };
         }
 
-        skvm::I32 pack_565(Color c) {
-            skvm::I32 r = div255(mul(c.r, splat(31))),
-                      g = div255(mul(c.g, splat(63))),
-                      b = div255(mul(c.b, splat(31)));
-            return pack(pack(b, g,5), r,11);
-        }
+        // If Builder can't build this program, CacheKey() sets *ok to false.
+        static Key CacheKey(const Params& params, skvm::Uniforms* uniforms, bool* ok) {
+            SkASSERT(params.shader);
+            uint32_t shaderHash = 0;
+            {
+                const SkShaderBase* shader = as_SB(params.shader);
+                skvm::Builder p;
+                skvm::F32 x = p.to_f32(p.sub(p.uniform32(uniforms->ptr,
+                                                         offsetof(BlitterUniforms, right)),
+                                             p.index())),
+                          y = p.to_f32(p.uniform32(uniforms->ptr,
+                                                   offsetof(BlitterUniforms, y)));
+                skvm::F32 r,g,b,a;
+                if (shader->program(&p,
+                                    params.colorSpace.get(),
+                                    uniforms,
+                                    x,y, &r,&g,&b,&a)) {
+                    shaderHash = p.hash();
+                } else {
+                    *ok = false;
+                }
+            }
 
-        // TODO: add native min/max ops to skvm::Builder
-        skvm::I32 min(skvm::I32 x, skvm::I32 y) { return select(lt(x,y), x,y); }
-        skvm::I32 max(skvm::I32 x, skvm::I32 y) { return select(gt(x,y), x,y); }
-
-        static bool CanBuild(const Key& key) {
-            // These checks parallel the TODOs in Builder::Builder().
-            if (key.shader)      { return false; }
-            if (key.colorFilter) { return false; }
-
-            switch (key.colorType) {
-                default: return false;
+            switch (params.colorType) {
+                default: *ok = false;        break;
                 case kRGB_565_SkColorType:   break;
                 case kRGBA_8888_SkColorType: break;
                 case kBGRA_8888_SkColorType: break;
             }
 
-            if (key.alphaType == kUnpremul_SkAlphaType) { return false; }
+            if (params.alphaType == kUnpremul_SkAlphaType) { *ok = false; }
 
-            switch (key.blendMode) {
-                default: return false;
-                case SkBlendMode::kSrc:     break;
-                case SkBlendMode::kSrcOver: break;
+            if (!skvm::BlendModeSupported(params.blendMode)) {
+                *ok = false;
             }
 
-            return true;
+            return {
+                params.colorSpace ? params.colorSpace->hash() : 0,
+                shaderHash,
+                SkToU8(params.colorType),
+                SkToU8(params.alphaType),
+                SkToU8(params.blendMode),
+                SkToU8(params.coverage),
+            };
         }
 
-        explicit Builder(const Key& key) {
+        Builder(const Params& params, skvm::Uniforms* uniforms) {
         #define TODO SkUNREACHABLE
-            SkASSERT(CanBuild(key));
-            skvm::Arg uniforms = uniform(),
-                      dst_ptr  = arg(SkColorTypeBytesPerPixel(key.colorType));
-            // When coverage is MaskA8 or MaskLCD16 there will be one more mask varying,
-            // and when coverage is Mask3D there will be three more mask varyings.
+            // First two arguments are always uniforms and the destination buffer.
+            uniforms->ptr     = uniform();
+            skvm::Arg dst_ptr = arg(SkColorTypeBytesPerPixel(params.colorType));
+            // Other arguments depend on params.coverage:
+            //    - Full:      (no more arguments)
+            //    - Mask3D:    mul varying, add varying, 8-bit coverage varying
+            //    - MaskA8:    8-bit coverage varying
+            //    - MaskLCD16: 565 coverage varying
+            //    - UniformA8: 8-bit coverage uniform
 
+            skvm::Color src;
+            SkASSERT(params.shader);
+            skvm::F32 x = to_f32(sub(uniform32(uniforms->ptr,
+                                               offsetof(BlitterUniforms, right)),
+                                     index())),
+                      y = to_f32(uniform32(uniforms->ptr,
+                                           offsetof(BlitterUniforms, y)));
+            SkAssertResult(as_SB(params.shader)->program(this,
+                                                         params.colorSpace.get(),
+                                                         uniforms,
+                                                         x,y, &src.r, &src.g, &src.b, &src.a));
+            // We don't know if the src color is normalized (logical [0,1], premul [0,a]) or not.
+            bool src_is_normalized = false;
 
-            // When there's no shader and no color filter, the source color is the paint color.
-            if (key.shader)      { TODO; }
-            if (key.colorFilter) { TODO; }
-            Color src = unpack_8888(uniform32(uniforms, offsetof(Uniforms, paint_color)));
+            if (params.coverage == Coverage::Mask3D) {
+                skvm::F32 M = unorm(8, load8(varying<uint8_t>())),
+                          A = unorm(8, load8(varying<uint8_t>()));
+
+                src.r = min(mad(src.r, M, A), src.a);
+                src.g = min(mad(src.g, M, A), src.a);
+                src.b = min(mad(src.b, M, A), src.a);
+            }
+
+            // Normalized premul formats can surprisingly represent some out-of-gamut
+            // values (e.g. r=0xff, a=0xee fits in unorm8 but r = 1.07), but most code
+            // working with normalized premul colors is not prepared to handle r,g,b > a.
+            // So we clamp the shader to gamut here before blending and coverage.
+            if (params.alphaType == kPremul_SkAlphaType
+                    && SkColorTypeIsNormalized(params.colorType)) {
+                src.r = min(max(splat(0.0f), src.r), src.a);
+                src.g = min(max(splat(0.0f), src.g), src.a);
+                src.b = min(max(splat(0.0f), src.b), src.a);
+
+                assert_true(gte(src.a, splat(0.0f)));
+                assert_true(lte(src.a, splat(1.0f)));
+
+                // Knowing that we're normalizing here and that blending and coverage
+                // won't affect that when the destination is normalized, we can avoid
+                // avoid a redundant clamp just before storing.
+                src_is_normalized = true;
+            }
 
             // There are several orderings here of when we load dst and coverage
             // and how coverage is applied, and to complicate things, LCD coverage
             // needs to know dst.a.  We're careful to assert it's loaded in time.
-            Color dst;
+            skvm::Color dst;
             SkDEBUGCODE(bool dst_loaded = false;)
 
             // load_coverage() returns false when there's no need to apply coverage.
-            auto load_coverage = [&](Color* cov) {
-                switch (key.coverage) {
+            auto load_coverage = [&](skvm::Color* cov) {
+                switch (params.coverage) {
                     case Coverage::Full: return false;
 
                     case Coverage::UniformA8: cov->r = cov->g = cov->b = cov->a =
-                                              uniform8(uniforms, offsetof(Uniforms, coverage));
+                                              unorm(8, uniform8(uniform()));
                                               return true;
 
+                    case Coverage::Mask3D:
                     case Coverage::MaskA8: cov->r = cov->g = cov->b = cov->a =
-                                           load8(varying<uint8_t>());
+                                           unorm(8, load8(varying<uint8_t>()));
                                            return true;
 
                     case Coverage::MaskLCD16:
@@ -208,8 +260,6 @@
                         cov->a = select(lt(src.a, dst.a), min(cov->r, min(cov->g,cov->b))
                                                         , max(cov->r, max(cov->g,cov->b)));
                         return true;
-
-                    case Coverage::Mask3D: TODO;
                 }
                 // GCC insists...
                 return false;
@@ -219,21 +269,21 @@
             // obviating the need for the lerp afterwards. This early-coverage strategy tends
             // to be both faster and require fewer registers.
             bool lerp_coverage_post_blend = true;
-            if (SkBlendMode_ShouldPreScaleCoverage(key.blendMode,
-                                                   key.coverage == Coverage::MaskLCD16)) {
-                Color cov;
+            if (SkBlendMode_ShouldPreScaleCoverage(params.blendMode,
+                                                   params.coverage == Coverage::MaskLCD16)) {
+                skvm::Color cov;
                 if (load_coverage(&cov)) {
-                    src.r = div255(mul(src.r, cov.r));
-                    src.g = div255(mul(src.g, cov.g));
-                    src.b = div255(mul(src.b, cov.b));
-                    src.a = div255(mul(src.a, cov.a));
+                    src.r = mul(src.r, cov.r);
+                    src.g = mul(src.g, cov.g);
+                    src.b = mul(src.b, cov.b);
+                    src.a = mul(src.a, cov.a);
                 }
                 lerp_coverage_post_blend = false;
             }
 
             // Load up the destination color.
             SkDEBUGCODE(dst_loaded = true;)
-            switch (key.colorType) {
+            switch (params.colorType) {
                 default: TODO;
                 case kRGB_565_SkColorType:   dst = unpack_565 (load16(dst_ptr)); break;
                 case kRGBA_8888_SkColorType: dst = unpack_8888(load32(dst_ptr)); break;
@@ -245,74 +295,137 @@
             // When a destination is tagged opaque, we may assume it both starts and stays fully
             // opaque, ignoring any math that disagrees.  So anything involving force_opaque is
             // optional, and sometimes helps cut a small amount of work in these programs.
-            const bool force_opaque = true && key.alphaType == kOpaque_SkAlphaType;
-            if (force_opaque) { dst.a = splat(0xff); }
+            const bool force_opaque = true && params.alphaType == kOpaque_SkAlphaType;
+            if (force_opaque) { dst.a = splat(1.0f); }
 
             // We'd need to premul dst after loading and unpremul before storing.
-            if (key.alphaType == kUnpremul_SkAlphaType) { TODO; }
+            if (params.alphaType == kUnpremul_SkAlphaType) { TODO; }
 
-            // Blend src and dst.
-            switch (key.blendMode) {
-                default: TODO;
-
-                case SkBlendMode::kSrc: break;
-
-                case SkBlendMode::kSrcOver: {
-                    src.r = add(src.r, div255(mul(dst.r, inv(src.a))));
-                    src.g = add(src.g, div255(mul(dst.g, inv(src.a))));
-                    src.b = add(src.b, div255(mul(dst.b, inv(src.a))));
-                    src.a = add(src.a, div255(mul(dst.a, inv(src.a))));
-                } break;
-            }
+            src = skvm::BlendModeProgram(this, params.blendMode, src, dst);
 
             // Lerp with coverage post-blend if needed.
-            Color cov;
+            skvm::Color cov;
             if (lerp_coverage_post_blend && load_coverage(&cov)) {
-                src.r = mix(dst.r, src.r, cov.r);
-                src.g = mix(dst.g, src.g, cov.g);
-                src.b = mix(dst.b, src.b, cov.b);
-                src.a = mix(dst.a, src.a, cov.a);
+                src.r = mad(sub(src.r, dst.r), cov.r, dst.r);
+                src.g = mad(sub(src.g, dst.g), cov.g, dst.g);
+                src.b = mad(sub(src.b, dst.b), cov.b, dst.b);
+                src.a = mad(sub(src.a, dst.a), cov.a, dst.a);
             }
 
-            if (force_opaque) { src.a = splat(0xff); }
+            // Clamp to fit destination color format if needed.
+            if (!src_is_normalized && SkColorTypeIsNormalized(params.colorType)) {
+                src.r = min(max(splat(0.0f), src.r), splat(1.0f));
+                src.g = min(max(splat(0.0f), src.g), splat(1.0f));
+                src.b = min(max(splat(0.0f), src.b), splat(1.0f));
+
+                assert_true(gte(src.a, splat(0.0f)));
+                assert_true(lte(src.a, splat(1.0f)));
+            }
+            if (force_opaque) { src.a = splat(1.0f); }
 
             // Store back to the destination.
-            switch (key.colorType) {
+            switch (params.colorType) {
                 default: SkUNREACHABLE;
 
-                case kRGB_565_SkColorType:   store16(dst_ptr, pack_565(src)); break;
+                case kRGB_565_SkColorType:
+                    store16(dst_ptr, pack(pack(unorm(5,src.b),
+                                               unorm(6,src.g), 5),
+                                               unorm(5,src.r),11));
+                    break;
 
                 case kBGRA_8888_SkColorType: std::swap(src.r, src.b);  // fallthrough
-                case kRGBA_8888_SkColorType: store32(dst_ptr, pack_8888(src)); break;
+                case kRGBA_8888_SkColorType:
+                     store32(dst_ptr, pack(pack(unorm(8, src.r),
+                                                unorm(8, src.g), 8),
+                                           pack(unorm(8, src.b),
+                                                unorm(8, src.a), 8), 16));
+                     break;
             }
         #undef TODO
         }
     };
 
+    // Scale the output of another shader by alpha.
+    struct AlphaShader : public SkShaderBase {
+        AlphaShader(sk_sp<SkShader> shader, float alpha)
+            : fShader(std::move(shader))
+            , fAlpha(alpha) {}
+
+        sk_sp<SkShader> fShader;
+        float           fAlpha;
+
+        bool onProgram(skvm::Builder* p,
+                       SkColorSpace* dstCS,
+                       skvm::Uniforms* uniforms,
+                       skvm::F32 x, skvm::F32 y,
+                       skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override {
+            if (as_SB(fShader)->program(p, dstCS,
+                                        uniforms,
+                                        x,y, r,g,b,a)) {
+                skvm::F32 A = p->uniformF(uniforms->pushF(fAlpha));
+                *r = p->mul(*r, A);
+                *g = p->mul(*g, A);
+                *b = p->mul(*b, A);
+                *a = p->mul(*a, A);
+                return true;
+            }
+            return false;
+        }
+
+        // Only created here, should never be flattened / unflattened.
+        Factory getFactory() const override { return nullptr; }
+        const char* getTypeName() const override { return "AlphaShader"; }
+    };
+
+    static Params effective_params(const SkPixmap& device, const SkPaint& paint) {
+        // Color filters have been handled for us by SkBlitter::Choose().
+        SkASSERT(!paint.getColorFilter());
+
+        // If there's no explicit shader, the paint color is the shader,
+        // but if there is a shader, it's modulated by the paint alpha.
+        sk_sp<SkShader> shader = paint.refShader();
+        if (!shader) {
+            shader = SkShaders::Color(paint.getColor4f(), nullptr);
+        } else if (paint.getAlphaf() < 1.0f) {
+            shader = sk_make_sp<AlphaShader>(std::move(shader), paint.getAlphaf());
+        }
+
+        // The most common blend mode is SrcOver, and it can be strength-reduced
+        // _greatly_ to Src mode when the shader is opaque.
+        SkBlendMode blendMode = paint.getBlendMode();
+        if (blendMode == SkBlendMode::kSrcOver && shader->isOpaque()) {
+            blendMode =  SkBlendMode::kSrc;
+        }
+
+        // In general all the information we use to make decisions here need to
+        // be reflected in Params and Key to make program caching sound, and it
+        // might appear that shader->isOpaque() is a property of the shader's
+        // uniforms than its fundamental program structure and so unsafe to use.
+        //
+        // Opacity is such a powerful property that SkShaderBase::program()
+        // forces opacity for any shader subclass that claims isOpaque(), so
+        // the opaque bit is strongly guaranteed to be part of the program and
+        // not just a property of the uniforms.  The shader program hash includes
+        // this information, making it safe to use anywhere in the blitter codegen.
+
+        return {
+            device.refColorSpace(),
+            std::move(shader),
+            device.colorType(),
+            device.alphaType(),
+            blendMode,
+            Coverage::Full,  // Placeholder... withCoverage() will change as needed.
+        };
+    }
+
     class Blitter final : public SkBlitter {
     public:
-        bool ok = false;
-
-        Blitter(const SkPixmap& device, const SkPaint& paint)
+        Blitter(const SkPixmap& device, const SkPaint& paint, bool* ok)
             : fDevice(device)
-            , fKey {
-                device.colorType(),
-                device.alphaType(),
-                Coverage::Full,
-                paint.getBlendMode(),
-                paint.getShader(),
-                paint.getColorFilter(),
-            }
-        {
-            SkColor4f color = paint.getColor4f();
-            SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
-                                   device.colorSpace(), kUnpremul_SkAlphaType}.apply(color.vec());
-
-            if (color.fitsInBytes() && Builder::CanBuild(fKey)) {
-                fUniforms.paint_color = color.premul().toBytes_RGBA();
-                ok = true;
-            }
-        }
+            , fUniforms(kBlitterUniformsCount)
+            , fParams(effective_params(device, paint))
+            , fKey(Builder::CacheKey(fParams, &fUniforms, ok))
+        {}
 
         ~Blitter() override {
             if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) {
@@ -329,6 +442,7 @@
                 cache_program(std::move(fBlitH),         Coverage::Full);
                 cache_program(std::move(fBlitAntiH),     Coverage::UniformA8);
                 cache_program(std::move(fBlitMaskA8),    Coverage::MaskA8);
+                cache_program(std::move(fBlitMask3D),    Coverage::Mask3D);
                 cache_program(std::move(fBlitMaskLCD16), Coverage::MaskLCD16);
 
                 release_program_cache();
@@ -336,13 +450,15 @@
         }
 
     private:
-        SkPixmap      fDevice;  // TODO: can this be const&?
-        const Key     fKey;
-        Uniforms      fUniforms;
-        skvm::Program fBlitH,
-                      fBlitAntiH,
-                      fBlitMaskA8,
-                      fBlitMaskLCD16;
+        SkPixmap       fDevice;  // TODO: can this be const&?
+        skvm::Uniforms fUniforms;
+        const Params   fParams;
+        const Key      fKey;
+        skvm::Program  fBlitH,
+                       fBlitAntiH,
+                       fBlitMaskA8,
+                       fBlitMask3D,
+                       fBlitMaskLCD16;
 
         skvm::Program buildProgram(Coverage coverage) {
             Key key = fKey.withCoverage(coverage);
@@ -358,27 +474,42 @@
                     return p;
                 }
             }
-        #if 0
-            static std::atomic<int> done{0};
-            if (0 == done++) {
-                atexit([]{ SkDebugf("%d calls to done\n", done.load()); });
-            }
-        #endif
-            Builder builder{key};
+            // We don't really _need_ to rebuild fUniforms here.
+            // It's just more natural to have effects unconditionally emit them,
+            // and more natural to rebuild fUniforms than to emit them into a dummy buffer.
+            // fUniforms should reuse the exact same memory, so this is very cheap.
+            SkDEBUGCODE(size_t prev = fUniforms.buf.size();)
+            fUniforms.buf.resize(kBlitterUniformsCount);
+            Builder builder{fParams.withCoverage(coverage), &fUniforms};
+            SkASSERT(fUniforms.buf.size() == prev);
+
             skvm::Program program = builder.done(debug_name(key).c_str());
-            if (!program.hasJIT() && debug_dump(key)) {
-                SkDebugf("\nfalling back to interpreter for blitter with this key.\n");
-                builder.dump();
-                program.dump();
+            if (debug_dump(key)) {
+                static std::atomic<int> done{0};
+                if (0 == done++) {
+                    atexit([]{ SkDebugf("%d calls to done\n", done.load()); });
+                }
+
+                if (!program.hasJIT()) {
+                    SkDebugf("\nfalling back to interpreter for blitter with this key.\n");
+                    builder.dump();
+                    program.dump();
+                }
             }
             return program;
         }
 
+        void updateUniforms(int right, int y) {
+            BlitterUniforms uniforms{right, y};
+            memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms));
+        }
+
         void blitH(int x, int y, int w) override {
             if (fBlitH.empty()) {
                 fBlitH = this->buildProgram(Coverage::Full);
             }
-            fBlitH.eval(w, &fUniforms, fDevice.addr(x,y));
+            this->updateUniforms(x+w, y);
+            fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y));
         }
 
         void blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) override {
@@ -386,8 +517,8 @@
                 fBlitAntiH = this->buildProgram(Coverage::UniformA8);
             }
             for (int16_t run = *runs; run > 0; run = *runs) {
-                fUniforms.coverage = *cov;
-                fBlitAntiH.eval(run, &fUniforms, fDevice.addr(x,y));
+                this->updateUniforms(x+run, y);
+                fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), cov);
 
                 x    += run;
                 runs += run;
@@ -405,7 +536,13 @@
             switch (mask.fFormat) {
                 default: SkUNREACHABLE;     // ARGB and SDF masks shouldn't make it here.
 
-                case SkMask::k3D_Format:    // TODO: the mul and add 3D mask planes too
+                case SkMask::k3D_Format:
+                    if (fBlitMask3D.empty()) {
+                        fBlitMask3D = this->buildProgram(Coverage::Mask3D);
+                    }
+                    program = &fBlitMask3D;
+                    break;
+
                 case SkMask::kA8_Format:
                     if (fBlitMaskA8.empty()) {
                         fBlitMaskA8 = this->buildProgram(Coverage::MaskA8);
@@ -424,10 +561,20 @@
             SkASSERT(program);
             if (program) {
                 for (int y = clip.top(); y < clip.bottom(); y++) {
-                    program->eval(clip.width(),
-                                  &fUniforms,
-                                  fDevice.addr(clip.left(), y),
-                                  mask.getAddr(clip.left(), y));
+                    int x = clip.left(),
+                        w = clip.width();
+                    void* dptr =        fDevice.writable_addr(x,y);
+                    auto  mptr = (const uint8_t*)mask.getAddr(x,y);
+                    this->updateUniforms(x+w,y);
+
+                    if (program == &fBlitMask3D) {
+                        size_t plane = mask.computeImageSize();
+                        program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane
+                                                                   , mptr + 2*plane
+                                                                   , mptr + 0*plane);
+                    } else {
+                        program->eval(w, fUniforms.buf.data(), dptr, mptr);
+                    }
                 }
             }
         }
@@ -435,12 +582,100 @@
 
 }  // namespace
 
+bool skvm::BlendModeSupported(SkBlendMode mode) {
+    return mode <= SkBlendMode::kScreen;
+}
+
+skvm::Color skvm::BlendModeProgram(skvm::Builder* p,
+                                   SkBlendMode mode, skvm::Color src, skvm::Color dst) {
+    auto mma = [&](skvm::F32 x, skvm::F32 y, skvm::F32 z, skvm::F32 w) {
+        return p->mad(x,y, p->mul(z,w));
+    };
+
+    auto inv = [&](skvm::F32 x) {
+        return p->sub(p->splat(1.0f), x);
+    };
+
+    switch (mode) {
+        default: SkASSERT(false); /*but also, for safety, fallthrough*/
+
+        case SkBlendMode::kClear: return {
+            p->splat(0.0f),
+            p->splat(0.0f),
+            p->splat(0.0f),
+            p->splat(0.0f),
+        };
+
+        case SkBlendMode::kSrc: return src;
+        case SkBlendMode::kDst: return dst;
+
+        case SkBlendMode::kDstOver: std::swap(src, dst); // fall-through
+        case SkBlendMode::kSrcOver: return {
+            p->mad(dst.r, inv(src.a), src.r),
+            p->mad(dst.g, inv(src.a), src.g),
+            p->mad(dst.b, inv(src.a), src.b),
+            p->mad(dst.a, inv(src.a), src.a),
+        };
+
+        case SkBlendMode::kDstIn: std::swap(src, dst); // fall-through
+        case SkBlendMode::kSrcIn: return {
+            p->mul(src.r, dst.a),
+            p->mul(src.g, dst.a),
+            p->mul(src.b, dst.a),
+            p->mul(src.a, dst.a),
+        };
+
+        case SkBlendMode::kDstOut: std::swap(src, dst); // fall-through
+        case SkBlendMode::kSrcOut: return {
+            p->mul(src.r, inv(dst.a)),
+            p->mul(src.g, inv(dst.a)),
+            p->mul(src.b, inv(dst.a)),
+            p->mul(src.a, inv(dst.a)),
+        };
+
+        case SkBlendMode::kDstATop: std::swap(src, dst); // fall-through
+        case SkBlendMode::kSrcATop: return {
+            mma(src.r, dst.a,  dst.r, inv(src.a)),
+            mma(src.g, dst.a,  dst.g, inv(src.a)),
+            mma(src.b, dst.a,  dst.b, inv(src.a)),
+            mma(src.a, dst.a,  dst.a, inv(src.a)),
+        };
+
+        case SkBlendMode::kXor: return {
+            mma(src.r, inv(dst.a),  dst.r, inv(src.a)),
+            mma(src.g, inv(dst.a),  dst.g, inv(src.a)),
+            mma(src.b, inv(dst.a),  dst.b, inv(src.a)),
+            mma(src.a, inv(dst.a),  dst.a, inv(src.a)),
+        };
+
+        case SkBlendMode::kPlus: return {
+            p->min(p->add(src.r, dst.r), p->splat(1.0f)),
+            p->min(p->add(src.g, dst.g), p->splat(1.0f)),
+            p->min(p->add(src.b, dst.b), p->splat(1.0f)),
+            p->min(p->add(src.a, dst.a), p->splat(1.0f)),
+        };
+
+        case SkBlendMode::kModulate: return {
+            p->mul(src.r, dst.r),
+            p->mul(src.g, dst.g),
+            p->mul(src.b, dst.b),
+            p->mul(src.a, dst.a),
+        };
+
+        case SkBlendMode::kScreen: return {
+            p->sub(p->add(src.r, dst.r), p->mul(src.r, dst.r)),
+            p->sub(p->add(src.g, dst.g), p->mul(src.g, dst.g)),
+            p->sub(p->add(src.b, dst.b), p->mul(src.b, dst.b)),
+            p->sub(p->add(src.a, dst.a), p->mul(src.a, dst.a)),
+        };
+    }
+}
 
 SkBlitter* SkCreateSkVMBlitter(const SkPixmap& device,
                                const SkPaint& paint,
                                const SkMatrix& ctm,
                                SkArenaAlloc* alloc) {
-    auto blitter = alloc->make<Blitter>(device, paint);
-    return blitter->ok ? blitter
-                       : nullptr;
+    bool ok = true;
+    auto blitter = alloc->make<Blitter>(device, paint, &ok);
+    return ok ? blitter : nullptr;
 }
diff --git a/src/core/SkVMBlitter.h b/src/core/SkVMBlitter.h
new file mode 100644
index 0000000..1e01fbe
--- /dev/null
+++ b/src/core/SkVMBlitter.h
@@ -0,0 +1,21 @@
+/*
+ * 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 SkVMBlitter_DEFINED
+#define SkVMBlitter_DEFINED
+
+#include "include/core/SkBlendMode.h"
+#include "src/core/SkVM.h"
+
+namespace skvm {
+    struct Color { skvm::F32 r,g,b,a; };
+
+    bool BlendModeSupported(SkBlendMode);
+    Color BlendModeProgram(Builder*, SkBlendMode, Color src, Color dst);
+}
+
+#endif
diff --git a/src/core/SkWriter32.h b/src/core/SkWriter32.h
index 3f7d305..ca0664e 100644
--- a/src/core/SkWriter32.h
+++ b/src/core/SkWriter32.h
@@ -23,7 +23,7 @@
 #include "include/private/SkTemplates.h"
 #include "include/private/SkTo.h"
 
-class SK_API SkWriter32 : SkNoncopyable {
+class SkWriter32 : SkNoncopyable {
 public:
     /**
      *  The caller can specify an initial block of storage, which the caller manages.
diff --git a/src/core/SkYUVMath.cpp b/src/core/SkYUVMath.cpp
index 091e694..2fee1ba 100644
--- a/src/core/SkYUVMath.cpp
+++ b/src/core/SkYUVMath.cpp
@@ -47,21 +47,36 @@
     1.000000f,  1.772000f,  0.000000f,  0.000000f, -0.889475f,
     0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
 };
+const float BT2020_rgb_to_yuv[] = {
+    0.225613f,  0.582282f,  0.050928f,  0.000000f,  0.062745f,
+   -0.122655f, -0.316560f,  0.439216f,  0.000000f,  0.501961f,
+    0.439216f, -0.403890f, -0.035326f,  0.000000f,  0.501961f,
+    0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float BT2020_yuv_to_rgb[] = {
+    1.164384f,  0.000000f,  1.678674f,  0.000000f, -0.915688f,
+    1.164384f, -0.187326f, -0.650424f,  0.000000f,  0.347458f,
+    1.164384f,  2.141772f,  0.000000f,  0.000000f, -1.148145f,
+    0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
 
 static_assert(kJPEG_SkYUVColorSpace   == 0, "");
 static_assert(kRec601_SkYUVColorSpace == 1, "");
 static_assert(kRec709_SkYUVColorSpace == 2, "");
+static_assert(kBT2020_SkYUVColorSpace == 3, "");
 
 const float* yuv_to_rgb_array[] = {
     JPEG_yuv_to_rgb,
     Rec601_yuv_to_rgb,
     Rec709_yuv_to_rgb,
+    BT2020_yuv_to_rgb,
 };
 
 const float* rgb_to_yuv_array[] = {
     JPEG_rgb_to_yuv,
     Rec601_rgb_to_yuv,
     Rec709_rgb_to_yuv,
+    BT2020_rgb_to_yuv,
 };
 
 constexpr size_t kSizeOfColorMatrix = 20 * sizeof(float);
@@ -127,7 +142,6 @@
 namespace {
 struct YUVCoeff {
     float   Kr, Kb;
-    float   Cr, Cb;
     float   scaleY, addY;
     float   scaleUV;
 };
@@ -135,19 +149,24 @@
 
 const YUVCoeff gCoeff[] = {
     // kJPEG_SkYUVColorSpace
-    { 0.299f,  0.114f,  1/1.772f,  1/1.402f,          1,        0,         1, },
+    { 0.299f,  0.114f,          1,        0,         1, },
 
     // kRec601_SkYUVColorSpace
-    { 0.299f,  0.114f,  1/1.772f,  1/1.402f,  219/255.f, 16/255.f, 224/255.f, },
+    { 0.299f,  0.114f,  219/255.f, 16/255.f, 224/255.f, },
 
     // kRec709_SkYUVColorSpace
-    { 0.2126f, 0.0722f, 1/1.8556f, 1/1.5748f, 219/255.f, 16/255.f, 224/255.f, },
+    { 0.2126f, 0.0722f, 219/255.f, 16/255.f, 224/255.f, },
+
+    // kBT2020_SkYUVColorSpace
+    { 0.2627f, 0.0593f, 219/255.f, 16/255.f, 224/255.f, },
 };
 
 static void make_rgb_to_yuv_matrix(float mx[20], const YUVCoeff& c) {
     const float Kr = c.Kr;
     const float Kb = c.Kb;
     const float Kg = 1.0f - Kr - Kb;
+    const float Cr = 0.5f / (1.0f - Kb);
+    const float Cb = 0.5f / (1.0f - Kr);
 
     float m[20] = {
           Kr,  Kg,   Kb,  0,    c.addY,
@@ -157,13 +176,13 @@
     };
     memcpy(mx, m, sizeof(m));
     scale3(mx +  0, c.scaleY);
-    scale3(mx +  5, c.Cr * c.scaleUV);
-    scale3(mx + 10, c.Cb * c.scaleUV);
+    scale3(mx +  5, Cr * c.scaleUV);
+    scale3(mx + 10, Cb * c.scaleUV);
 }
 
 static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) {
     const char* names[] = {
-        "JPEG", "Rec601", "Rec709",
+        "JPEG", "Rec601", "Rec709", "BT2020",
     };
     const char* dirnames[] = {
         "yuv_to_rgb", "rgb_to_yuv",
@@ -182,7 +201,8 @@
 // Used to create the prebuilt tables for each colorspace.
 // Don't remove this function, in case we want to recompute those tables in the future.
 void SkColorMatrix_DumpYUVMatrixTables() {
-    for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
+    for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace,
+                    kBT2020_SkYUVColorSpace}) {
         float m[20];
         make_rgb_to_yuv_matrix(m, gCoeff[(unsigned)cs]);
         dump(m, cs, true);
diff --git a/src/core/SkZip.h b/src/core/SkZip.h
index 1249fbb..5561566 100644
--- a/src/core/SkZip.h
+++ b/src/core/SkZip.h
@@ -82,8 +82,20 @@
     constexpr std::tuple<Ts*...> data() const { return fPointers; }
     constexpr SkZip first(size_t n) const {
         SkASSERT(n <= this->size());
+        if (n == 0) { return SkZip(); }
         return SkZip{n, fPointers};
     }
+    constexpr SkZip last(size_t n) const {
+        SkASSERT(n <= this->size());
+        if (n == 0) { return SkZip(); }
+        return SkZip{n, this->pointersAt(fSize - n)};
+    }
+    constexpr SkZip subspan(size_t offset, size_t count) const {
+        SkASSERT(offset < this->size());
+        SkASSERT(count <= this->size() - offset);
+        if (count == 0) { return SkZip(); }
+        return SkZip(count, pointersAt(offset));
+    }
 
 private:
     constexpr SkZip(size_t n, const std::tuple<Ts*...>& pointers)
@@ -101,6 +113,17 @@
         return ReturnTuple((std::get<Is>(fPointers))[i]...);
     }
 
+    std::tuple<Ts*...> pointersAt(size_t i) const {
+        SkASSERT(this->size() > 0);
+        SkASSERT(i < this->size());
+        return pointersAtDetail(i, skstd::make_index_sequence<sizeof...(Ts)>{});
+    }
+
+    template<std::size_t... Is>
+    constexpr std::tuple<Ts*...> pointersAtDetail(size_t i, skstd::index_sequence<Is...>) const {
+        return std::tuple<Ts*...>{&(std::get<Is>(fPointers))[i]...};
+    }
+
     std::tuple<Ts*...> fPointers;
     size_t fSize;
 };
diff --git a/src/effects/SkColorMatrix.cpp b/src/effects/SkColorMatrix.cpp
index f6d8eaed..602b018 100644
--- a/src/effects/SkColorMatrix.cpp
+++ b/src/effects/SkColorMatrix.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "include/effects/SkColorMatrix.h"
-#include "include/private/SkFloatingPoint.h"
 
 enum {
     kR_Scale = 0,
@@ -46,17 +45,17 @@
     }
 
     if (target != result) {
-        memcpy(result, target, 20 * sizeof(float));
+        std::copy_n(target, 20, result);
     }
 }
 
 void SkColorMatrix::setIdentity() {
-    memset(fMat, 0, sizeof(fMat));
+    fMat.fill(0.0f);
     fMat[kR_Scale] = fMat[kG_Scale] = fMat[kB_Scale] = fMat[kA_Scale] = 1;
 }
 
 void SkColorMatrix::setScale(float rScale, float gScale, float bScale, float aScale) {
-    memset(fMat, 0, sizeof(fMat));
+    fMat.fill(0.0f);
     fMat[kR_Scale] = rScale;
     fMat[kG_Scale] = gScale;
     fMat[kB_Scale] = bScale;
@@ -72,42 +71,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkColorMatrix::setRotate(Axis axis, float degrees) {
-    float r = sk_float_degrees_to_radians(degrees);
-    this->setSinCos(axis, sk_float_sin(r), sk_float_cos(r));
-}
-
-void SkColorMatrix::setSinCos(Axis axis, float sine, float cosine) {
-    SkASSERT((unsigned)axis < 3);
-
-    static const uint8_t gRotateIndex[] = {
-        6, 7, 11, 12,
-        0, 10, 2, 12,
-        0, 1,  5,  6,
-    };
-    const uint8_t* index = gRotateIndex + axis * 4;
-
-    this->setIdentity();
-    fMat[index[0]] = cosine;
-    fMat[index[1]] = sine;
-    fMat[index[2]] = -sine;
-    fMat[index[3]] = cosine;
-}
-
-void SkColorMatrix::preRotate(Axis axis, float degrees) {
-    SkColorMatrix tmp;
-    tmp.setRotate(axis, degrees);
-    this->preConcat(tmp);
-}
-
-void SkColorMatrix::postRotate(Axis axis, float degrees) {
-    SkColorMatrix tmp;
-    tmp.setRotate(axis, degrees);
-    this->postConcat(tmp);
-}
-
 void SkColorMatrix::setConcat(const SkColorMatrix& matA, const SkColorMatrix& matB) {
-    set_concat(fMat, matA.fMat, matB.fMat);
+    set_concat(fMat.data(), matA.fMat.data(), matB.fMat.data());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -123,49 +88,14 @@
 static const float kHueB = 0.072f;
 
 void SkColorMatrix::setSaturation(float sat) {
-    memset(fMat, 0, sizeof(fMat));
+    fMat.fill(0.0f);
 
     const float R = kHueR * (1 - sat);
     const float G = kHueG * (1 - sat);
     const float B = kHueB * (1 - sat);
 
-    setrow(fMat +  0, R + sat, G, B);
-    setrow(fMat +  5, R, G + sat, B);
-    setrow(fMat + 10, R, G, B + sat);
-    fMat[kA_Scale] = 1;
-}
-
-static const float kR2Y = 0.299f;
-static const float kG2Y = 0.587f;
-static const float kB2Y = 0.114f;
-
-static const float kR2U = -0.16874f;
-static const float kG2U = -0.33126f;
-static const float kB2U = 0.5f;
-
-static const float kR2V = 0.5f;
-static const float kG2V = -0.41869f;
-static const float kB2V = -0.08131f;
-
-void SkColorMatrix::setRGB2YUV() {
-    memset(fMat, 0, sizeof(fMat));
-
-    setrow(fMat +  0, kR2Y, kG2Y, kB2Y);
-    setrow(fMat +  5, kR2U, kG2U, kB2U);
-    setrow(fMat + 10, kR2V, kG2V, kB2V);
-    fMat[kA_Scale] = 1;
-}
-
-static const float kV2R = 1.402f;
-static const float kU2G = -0.34414f;
-static const float kV2G = -0.71414f;
-static const float kU2B = 1.772f;
-
-void SkColorMatrix::setYUV2RGB() {
-    memset(fMat, 0, sizeof(fMat));
-
-    setrow(fMat +  0, 1, 0, kV2R);
-    setrow(fMat +  5, 1, kU2G, kV2G);
-    setrow(fMat + 10, 1, kU2B, 0);
+    setrow(fMat.data() +  0, R + sat, G, B);
+    setrow(fMat.data() +  5, R, G + sat, B);
+    setrow(fMat.data() + 10, R, G, B + sat);
     fMat[kA_Scale] = 1;
 }
diff --git a/src/effects/SkEmbossMaskFilter.h b/src/effects/SkEmbossMaskFilter.h
index d79e89b..bddf601 100644
--- a/src/effects/SkEmbossMaskFilter.h
+++ b/src/effects/SkEmbossMaskFilter.h
@@ -14,7 +14,7 @@
 
     This mask filter creates a 3D emboss look, by specifying a light and blur amount.
 */
-class SK_API SkEmbossMaskFilter : public SkMaskFilterBase {
+class SkEmbossMaskFilter : public SkMaskFilterBase {
 public:
     struct Light {
         SkScalar    fDirection[3];  // x,y,z
diff --git a/src/effects/SkHighContrastFilter.cpp b/src/effects/SkHighContrastFilter.cpp
index 5382a03..4fbbf48 100644
--- a/src/effects/SkHighContrastFilter.cpp
+++ b/src/effects/SkHighContrastFilter.cpp
@@ -66,7 +66,7 @@
     // Linearize before applying high-contrast filter.
     auto tf = alloc->make<skcms_TransferFunction>();
     if (rec.fDstCS) {
-        rec.fDstCS->transferFn(&tf->g);
+        rec.fDstCS->transferFn(tf);
     } else {
         // Historically we approximate untagged destinations as gamma 2.
         // TODO: sRGB?
@@ -115,7 +115,7 @@
     // Re-encode back from linear.
     auto invTF = alloc->make<skcms_TransferFunction>();
     if (rec.fDstCS) {
-        rec.fDstCS->invTransferFn(&invTF->g);
+        rec.fDstCS->invTransferFn(invTF);
     } else {
         // See above... historically untagged == gamma 2 in this filter.
         *invTF ={0.5f,1, 0,0,0,0,0};
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 601da8f..576fac6 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -264,7 +264,7 @@
 
     bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
 
-    ColorTableEffect(sk_sp<GrTextureProxy> proxy)
+    ColorTableEffect(sk_sp<GrSurfaceProxy> proxy)
             : INHERITED(kColorTableEffect_ClassID,
                         kNone_OptimizationFlags) // Not bothering with table-specific optimizations.
             , fTextureSampler(std::move(proxy)) {
diff --git a/src/effects/imagefilters/SkAlphaThresholdFilter.cpp b/src/effects/imagefilters/SkAlphaThresholdFilter.cpp
index a059b65..c097347 100644
--- a/src/effects/imagefilters/SkAlphaThresholdFilter.cpp
+++ b/src/effects/imagefilters/SkAlphaThresholdFilter.cpp
@@ -166,10 +166,9 @@
             return nullptr;
         }
 
-        GrColorType srcColorType = SkColorTypeToGrColorType(input->colorType());
-        auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy), srcColorType,
-                                                     SkMatrix::MakeTrans(input->subset().x(),
-                                                                         input->subset().y()));
+        auto textureFP = GrSimpleTextureEffect::Make(
+                std::move(inputProxy), input->alphaType(),
+                SkMatrix::MakeTrans(input->subset().x(), input->subset().y()));
         textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
                                                   input->alphaType(), ctx.colorSpace());
         if (!textureFP) {
diff --git a/src/effects/imagefilters/SkArithmeticImageFilter.cpp b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
index 2b1aaaf..42c62be 100644
--- a/src/effects/imagefilters/SkArithmeticImageFilter.cpp
+++ b/src/effects/imagefilters/SkArithmeticImageFilter.cpp
@@ -353,11 +353,12 @@
         SkMatrix backgroundMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
                 SkIntToScalar(bgSubset.top()  - backgroundOffset.fY));
-        GrColorType bgColorType = SkColorTypeToGrColorType(background->colorType());
-        bgFP = GrTextureDomainEffect::Make(
-                std::move(backgroundProxy), bgColorType, backgroundMatrix,
+        bgFP = GrSimpleTextureEffect::Make(std::move(backgroundProxy), background->alphaType(),
+                                           backgroundMatrix, GrSamplerState::Filter::kNearest);
+        bgFP = GrDomainEffect::Make(
+                std::move(bgFP),
                 GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
-                GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
+                GrTextureDomain::kDecal_Mode, false);
         bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
                                              background->alphaType(),
                                              ctx.colorSpace());
@@ -371,11 +372,13 @@
         SkMatrix foregroundMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
                 SkIntToScalar(fgSubset.top()  - foregroundOffset.fY));
-        GrColorType fgColorType = SkColorTypeToGrColorType(foreground->colorType());
-        auto foregroundFP = GrTextureDomainEffect::Make(
-                std::move(foregroundProxy), fgColorType, foregroundMatrix,
+        auto foregroundFP =
+                GrSimpleTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(),
+                                            foregroundMatrix, GrSamplerState::Filter::kNearest);
+        foregroundFP = GrDomainEffect::Make(
+                std::move(foregroundFP),
                 GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
-                GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
+                GrTextureDomain::kDecal_Mode, false);
         foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
                                                      foreground->getColorSpace(),
                                                      foreground->alphaType(),
diff --git a/src/effects/imagefilters/SkDisplacementMapEffect.cpp b/src/effects/imagefilters/SkDisplacementMapEffect.cpp
index 8b4284d..f18072b 100644
--- a/src/effects/imagefilters/SkDisplacementMapEffect.cpp
+++ b/src/effects/imagefilters/SkDisplacementMapEffect.cpp
@@ -648,11 +648,11 @@
 void GrGLDisplacementMapEffect::onSetData(const GrGLSLProgramDataManager& pdman,
                                           const GrFragmentProcessor& proc) {
     const GrDisplacementMapEffect& displacementMap = proc.cast<GrDisplacementMapEffect>();
-    GrTextureProxy* proxy = displacementMap.textureSampler(1).proxy();
-    GrTexture* colorTex = proxy->peekTexture();
+    GrSurfaceProxy* proxy = displacementMap.textureSampler(1).proxy();
+    SkISize texDimensions = proxy->backingStoreDimensions();
 
-    SkScalar scaleX = displacementMap.scale().fX / colorTex->width();
-    SkScalar scaleY = displacementMap.scale().fY / colorTex->height();
+    SkScalar scaleX = displacementMap.scale().fX / texDimensions.width();
+    SkScalar scaleY = displacementMap.scale().fY / texDimensions.height();
     pdman.set2f(fScaleUni, SkScalarToFloat(scaleX),
                 proxy->origin() == kTopLeft_GrSurfaceOrigin ?
                 SkScalarToFloat(scaleY) : SkScalarToFloat(-scaleY));
diff --git a/src/effects/imagefilters/SkLightingImageFilter.cpp b/src/effects/imagefilters/SkLightingImageFilter.cpp
index 43d32dc..e18e7ab 100644
--- a/src/effects/imagefilters/SkLightingImageFilter.cpp
+++ b/src/effects/imagefilters/SkLightingImageFilter.cpp
@@ -1840,11 +1840,11 @@
         fLight = lighting.light()->createGLLight();
     }
 
-    GrTextureProxy* proxy = lighting.textureSampler(0).proxy();
-    GrTexture* texture = proxy->peekTexture();
+    GrSurfaceProxy* proxy = lighting.textureSampler(0).proxy();
+    SkISize textureDims = proxy->backingStoreDimensions();
 
     float ySign = proxy->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
-    pdman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
+    pdman.set2f(fImageIncrementUni, 1.0f / textureDims.width(), ySign / textureDims.height());
     pdman.set1f(fSurfaceScaleUni, lighting.surfaceScale());
     sk_sp<SkImageFilterLight> transformedLight(
             lighting.light()->transform(lighting.filterMatrix()));
diff --git a/src/effects/imagefilters/SkMorphologyImageFilter.cpp b/src/effects/imagefilters/SkMorphologyImageFilter.cpp
index 47e563b..b039089 100644
--- a/src/effects/imagefilters/SkMorphologyImageFilter.cpp
+++ b/src/effects/imagefilters/SkMorphologyImageFilter.cpp
@@ -210,20 +210,18 @@
 class GrMorphologyEffect : public GrFragmentProcessor {
 public:
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
-                                                     MorphDirection dir,
+                                                     SkAlphaType srcAlphaType, MorphDirection dir,
                                                      int radius, MorphType type) {
         return std::unique_ptr<GrFragmentProcessor>(
-                new GrMorphologyEffect(std::move(proxy), srcColorType, dir, radius, type, nullptr));
+                new GrMorphologyEffect(std::move(proxy), srcAlphaType, dir, radius, type, nullptr));
     }
 
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
-                                                     MorphDirection dir,
+                                                     SkAlphaType srcAlphaType, MorphDirection dir,
                                                      int radius, MorphType type,
                                                      const float bounds[2]) {
         return std::unique_ptr<GrFragmentProcessor>(
-                new GrMorphologyEffect(std::move(proxy), srcColorType, dir, radius, type, bounds));
+                new GrMorphologyEffect(std::move(proxy), srcAlphaType, dir, radius, type, bounds));
     }
 
     MorphType type() const { return fType; }
@@ -256,7 +254,7 @@
 
     const TextureSampler& onTextureSampler(int i) const override { return fTextureSampler; }
 
-    GrMorphologyEffect(sk_sp<GrTextureProxy>, GrColorType srcColorType, MorphDirection, int radius,
+    GrMorphologyEffect(sk_sp<GrTextureProxy>, SkAlphaType srcAlphaType, MorphDirection, int radius,
                        MorphType, const float range[2]);
     explicit GrMorphologyEffect(const GrMorphologyEffect&);
 
@@ -395,13 +393,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrMorphologyEffect::GrMorphologyEffect(sk_sp<GrTextureProxy> proxy,
-                                       GrColorType srcColorType,
+                                       SkAlphaType srcAlphaType,
                                        MorphDirection direction,
                                        int radius,
                                        MorphType type,
                                        const float range[2])
-        : INHERITED(kGrMorphologyEffect_ClassID,
-                    ModulateForClampedSamplerOptFlags(srcColorType))
+        : INHERITED(kGrMorphologyEffect_ClassID, ModulateForClampedSamplerOptFlags(srcAlphaType))
         , fCoordTransform(proxy.get())
         , fTextureSampler(std::move(proxy))
         , fDirection(direction)
@@ -465,16 +462,16 @@
     static const int kMaxRadius = 10;
     int radius = d->fRandom->nextRangeU(1, kMaxRadius);
     MorphType type = d->fRandom->nextBool() ? MorphType::kErode : MorphType::kDilate;
-
-    return GrMorphologyEffect::Make(std::move(proxy), d->textureProxyColorType(texIdx), dir, radius,
-                                    type);
+    auto alphaType = static_cast<SkAlphaType>(
+            d->fRandom->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
+    return GrMorphologyEffect::Make(std::move(proxy), alphaType, dir, radius, type);
 }
 #endif
 
 static void apply_morphology_rect(GrRenderTargetContext* renderTargetContext,
                                   const GrClip& clip,
                                   sk_sp<GrTextureProxy> proxy,
-                                  GrColorType srcColorType,
+                                  SkAlphaType srcAlphaType,
                                   const SkIRect& srcRect,
                                   const SkIRect& dstRect,
                                   int radius,
@@ -482,9 +479,8 @@
                                   const float bounds[2],
                                   MorphDirection direction) {
     GrPaint paint;
-    paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy), srcColorType,
-                                                             direction, radius, morphType,
-                                                             bounds));
+    paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy), srcAlphaType,
+                                                             direction, radius, morphType, bounds));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
                                         SkRect::Make(dstRect), SkRect::Make(srcRect));
@@ -493,15 +489,15 @@
 static void apply_morphology_rect_no_bounds(GrRenderTargetContext* renderTargetContext,
                                             const GrClip& clip,
                                             sk_sp<GrTextureProxy> proxy,
-                                            GrColorType srcColorType,
+                                            SkAlphaType srcAlphaType,
                                             const SkIRect& srcRect,
                                             const SkIRect& dstRect,
                                             int radius,
                                             MorphType morphType,
                                             MorphDirection direction) {
     GrPaint paint;
-    paint.addColorFragmentProcessor(GrMorphologyEffect::Make(std::move(proxy), srcColorType,
-                                                             direction, radius, morphType));
+    paint.addColorFragmentProcessor(
+            GrMorphologyEffect::Make(std::move(proxy), srcAlphaType, direction, radius, morphType));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
                                         SkRect::Make(dstRect), SkRect::Make(srcRect));
@@ -510,7 +506,7 @@
 static void apply_morphology_pass(GrRenderTargetContext* renderTargetContext,
                                   const GrClip& clip,
                                   sk_sp<GrTextureProxy> textureProxy,
-                                  GrColorType srcColorType,
+                                  SkAlphaType srcAlphaType,
                                   const SkIRect& srcRect,
                                   const SkIRect& dstRect,
                                   int radius,
@@ -541,16 +537,16 @@
     }
     if (middleSrcRect.width() <= 0) {
         // radius covers srcRect; use bounds over entire draw
-        apply_morphology_rect(renderTargetContext, clip, std::move(textureProxy), srcColorType,
+        apply_morphology_rect(renderTargetContext, clip, std::move(textureProxy), srcAlphaType,
                               srcRect, dstRect, radius, morphType, bounds, direction);
     } else {
         // Draw upper and lower margins with bounds; middle without.
-        apply_morphology_rect(renderTargetContext, clip, textureProxy, srcColorType,
-                              lowerSrcRect, lowerDstRect, radius, morphType, bounds, direction);
-        apply_morphology_rect(renderTargetContext, clip, textureProxy, srcColorType,
-                              upperSrcRect, upperDstRect, radius, morphType, bounds, direction);
+        apply_morphology_rect(renderTargetContext, clip, textureProxy, srcAlphaType, lowerSrcRect,
+                              lowerDstRect, radius, morphType, bounds, direction);
+        apply_morphology_rect(renderTargetContext, clip, textureProxy, srcAlphaType, upperSrcRect,
+                              upperDstRect, radius, morphType, bounds, direction);
         apply_morphology_rect_no_bounds(renderTargetContext, clip, std::move(textureProxy),
-                                        srcColorType, middleSrcRect, middleDstRect, radius,
+                                        srcAlphaType, middleSrcRect, middleDstRect, radius,
                                         morphType, direction);
     }
 }
@@ -559,7 +555,7 @@
         GrRecordingContext* context, SkSpecialImage* input, const SkIRect& rect,
         MorphType morphType, SkISize radius, const SkImageFilter_Base::Context& ctx) {
     sk_sp<GrTextureProxy> srcTexture(input->asTextureProxyRef(context));
-    GrColorType srcColorType = SkColorTypeToGrColorType(input->colorType());
+    SkAlphaType srcAlphaType = input->alphaType();
     SkASSERT(srcTexture);
     sk_sp<SkColorSpace> colorSpace = ctx.refColorSpace();
     GrColorType colorType = ctx.grColorType();
@@ -590,7 +586,7 @@
             return nullptr;
         }
 
-        apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcColorType,
+        apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcAlphaType,
                               srcRect, dstRect, radius.fWidth, morphType, MorphDirection::kX);
         SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
                                               dstRect.width(), radius.fHeight);
@@ -599,7 +595,7 @@
         dstRTContext->clear(&clearRect, clearColor, GrRenderTargetContext::CanClearFullscreen::kNo);
 
         srcTexture = dstRTContext->asTextureProxyRef();
-        srcColorType = colorType;
+        srcAlphaType = dstRTContext->colorInfo().alphaType();
         srcRect = dstRect;
     }
     if (radius.fHeight > 0) {
@@ -619,7 +615,7 @@
             return nullptr;
         }
 
-        apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcColorType,
+        apply_morphology_pass(dstRTContext.get(), clip, std::move(srcTexture), srcAlphaType,
                               srcRect, dstRect, radius.fHeight, morphType, MorphDirection::kY);
 
         srcTexture = dstRTContext->asTextureProxyRef();
@@ -766,7 +762,10 @@
     int width = SkScalarFloorToInt(radius.width());
     int height = SkScalarFloorToInt(radius.height());
 
-    if (width < 0 || height < 0) {
+    // Width (or height) must fit in a signed 32-bit int to avoid UBSAN issues (crbug.com/1018190)
+    constexpr int kMaxRadius = (std::numeric_limits<int>::max() - 1) / 2;
+
+    if (width < 0 || height < 0 || width > kMaxRadius || height > kMaxRadius) {
         return nullptr;
     }
 
diff --git a/src/effects/imagefilters/SkXfermodeImageFilter.cpp b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
index 14cf52f..b0b3d6b 100644
--- a/src/effects/imagefilters/SkXfermodeImageFilter.cpp
+++ b/src/effects/imagefilters/SkXfermodeImageFilter.cpp
@@ -266,11 +266,12 @@
         SkMatrix bgMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(bgSubset.left() - backgroundOffset.fX),
                 SkIntToScalar(bgSubset.top()  - backgroundOffset.fY));
-        GrColorType bgColorType = SkColorTypeToGrColorType(background->colorType());
-        bgFP = GrTextureDomainEffect::Make(
-                    std::move(backgroundProxy), bgColorType, bgMatrix,
-                    GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
-                    GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
+        bgFP = GrSimpleTextureEffect::Make(std::move(backgroundProxy), background->alphaType(),
+                                           bgMatrix, GrSamplerState::Filter::kNearest);
+        bgFP = GrDomainEffect::Make(
+                std::move(bgFP),
+                GrTextureDomain::MakeTexelDomain(bgSubset, GrTextureDomain::kDecal_Mode),
+                GrTextureDomain::kDecal_Mode, false);
         bgFP = GrColorSpaceXformEffect::Make(std::move(bgFP), background->getColorSpace(),
                                              background->alphaType(),
                                              ctx.colorSpace());
@@ -284,11 +285,13 @@
         SkMatrix fgMatrix = SkMatrix::MakeTrans(
                 SkIntToScalar(fgSubset.left() - foregroundOffset.fX),
                 SkIntToScalar(fgSubset.top()  - foregroundOffset.fY));
-        GrColorType fgColorType = SkColorTypeToGrColorType(foreground->colorType());
-        auto foregroundFP = GrTextureDomainEffect::Make(
-                std::move(foregroundProxy), fgColorType, fgMatrix,
+        auto foregroundFP =
+                GrSimpleTextureEffect::Make(std::move(foregroundProxy), foreground->alphaType(),
+                                            fgMatrix, GrSamplerState::Filter::kNearest);
+        foregroundFP = GrDomainEffect::Make(
+                std::move(foregroundFP),
                 GrTextureDomain::MakeTexelDomain(fgSubset, GrTextureDomain::kDecal_Mode),
-                GrTextureDomain::kDecal_Mode, GrSamplerState::Filter::kNearest);
+                GrTextureDomain::kDecal_Mode, false);
         foregroundFP = GrColorSpaceXformEffect::Make(std::move(foregroundFP),
                                                      foreground->getColorSpace(),
                                                      foreground->alphaType(),
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
index 34b9c07..dd97f03 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.cpp
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -214,9 +214,8 @@
 
     GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
 
-    GrColorType grColorType = SkColorTypeToGrColorType(this->getInfo().colorType());
-    return GrSurfaceProxy::Copy(context, texProxy.get(), grColorType, mipMapped, subset,
-                                SkBackingFit::kExact, SkBudgeted::kYes);
+    return GrSurfaceProxy::Copy(context, texProxy.get(), mipMapped, subset, SkBackingFit::kExact,
+                                SkBudgeted::kYes);
 }
 
 bool GrAHardwareBufferImageGenerator::onIsValid(GrContext* context) const {
diff --git a/src/gpu/GrBackendSurface.cpp b/src/gpu/GrBackendSurface.cpp
index fa95520..1b1d1f2 100644
--- a/src/gpu/GrBackendSurface.cpp
+++ b/src/gpu/GrBackendSurface.cpp
@@ -62,6 +62,7 @@
     }
 }
 
+#ifdef SK_GL
 GrBackendFormat::GrBackendFormat(GrGLenum format, GrGLenum target)
         : fBackend(GrBackendApi::kOpenGL)
         , fValid(true)
@@ -90,6 +91,7 @@
     }
     return GrGLFormat::kUnknown;
 }
+#endif
 
 GrBackendFormat GrBackendFormat::MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo) {
     SkASSERT(ycbcrInfo.isValid());
@@ -128,14 +130,14 @@
 }
 
 #ifdef SK_DAWN
-GrBackendFormat::GrBackendFormat(dawn::TextureFormat format)
+GrBackendFormat::GrBackendFormat(wgpu::TextureFormat format)
         : fBackend(GrBackendApi::kDawn)
         , fValid(true)
         , fDawnFormat(format)
         , fTextureType(GrTextureType::k2D) {
 }
 
-bool GrBackendFormat::asDawnFormat(dawn::TextureFormat* format) const {
+bool GrBackendFormat::asDawnFormat(wgpu::TextureFormat* format) const {
     SkASSERT(format);
     if (this->isValid() && GrBackendApi::kDawn == fBackend) {
         *format = fDawnFormat;
@@ -202,24 +204,27 @@
     }
 
     switch (fBackend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL:
             return fGLFormat == that.fGLFormat;
-        case GrBackendApi::kVulkan:
+            break;
+#endif
 #ifdef SK_VULKAN
+        case GrBackendApi::kVulkan:
             return fVk.fFormat == that.fVk.fFormat &&
                    fVk.fYcbcrConversionInfo == that.fVk.fYcbcrConversionInfo;
-#endif
             break;
+#endif
 #ifdef SK_METAL
         case GrBackendApi::kMetal:
             return fMtlFormat == that.fMtlFormat;
-#endif
             break;
-        case GrBackendApi::kDawn:
+#endif
 #ifdef SK_DAWN
+        case GrBackendApi::kDawn:
             return fDawnFormat == that.fDawnFormat;
-#endif
             break;
+#endif
         case GrBackendApi::kMock:
             return fMockColorType == that.fMockColorType;
         default:
@@ -347,6 +352,7 @@
         , fMtlInfo(mtlInfo) {}
 #endif
 
+#ifdef SK_GL
 GrBackendTexture::GrBackendTexture(int width,
                                    int height,
                                    GrMipMapped mipMapped,
@@ -355,6 +361,7 @@
     // Make no assumptions about client's texture's parameters.
     this->glTextureParametersModified();
 }
+#endif
 
 GrBackendTexture::GrBackendTexture(int width,
                                    int height,
@@ -691,6 +698,7 @@
         , fMtlInfo(mtlInfo) {}
 #endif
 
+#ifdef SK_GL
 GrBackendRenderTarget::GrBackendRenderTarget(int width,
                                              int height,
                                              int sampleCnt,
@@ -704,6 +712,7 @@
         , fGLInfo(glInfo) {
     fIsValid = SkToBool(glInfo.fFormat); // the glInfo must have a valid format
 }
+#endif
 
 GrBackendRenderTarget::GrBackendRenderTarget(int width,
                                              int height,
@@ -749,14 +758,16 @@
     fBackend = that.fBackend;
 
     switch (that.fBackend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL:
             fGLInfo = that.fGLInfo;
             break;
-        case GrBackendApi::kVulkan:
-#ifdef SK_VULKAN
-            fVkInfo.assign(that.fVkInfo, this->isValid());
 #endif
+#ifdef SK_VULKAN
+        case GrBackendApi::kVulkan:
+            fVkInfo.assign(that.fVkInfo, this->isValid());
             break;
+#endif
 #ifdef SK_DAWN
         case GrBackendApi::kDawn:
             fDawnInfo = that.fDawnInfo;
@@ -824,6 +835,7 @@
 }
 #endif
 
+#ifdef SK_GL
 bool GrBackendRenderTarget::getGLFramebufferInfo(GrGLFramebufferInfo* outInfo) const {
     if (this->isValid() && GrBackendApi::kOpenGL == fBackend) {
         *outInfo = fGLInfo;
@@ -831,6 +843,7 @@
     }
     return false;
 }
+#endif
 
 GrBackendFormat GrBackendRenderTarget::getBackendFormat() const {
     if (!this->isValid()) {
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index bd4cacb..5ab6e6c 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -24,11 +24,13 @@
 #include "src/gpu/SkGr.h"
 #include "src/gpu/gl/GrGLTexture.h"
 
-GrBackendTextureImageGenerator::RefHelper::RefHelper(GrTexture* texture, uint32_t owningContextID)
+GrBackendTextureImageGenerator::RefHelper::RefHelper(GrTexture* texture, uint32_t owningContextID,
+                                                     std::unique_ptr<GrSemaphore> semaphore)
         : fOriginalTexture(texture)
         , fOwningContextID(owningContextID)
         , fBorrowingContextReleaseProc(nullptr)
-        , fBorrowingContextID(SK_InvalidGenID) {}
+        , fBorrowingContextID(SK_InvalidGenID)
+        , fSemaphore(std::move(semaphore)) {}
 
 GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
     SkASSERT(fBorrowingContextID == SK_InvalidUniqueID);
@@ -41,7 +43,7 @@
 
 std::unique_ptr<SkImageGenerator>
 GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin,
-                                     sk_sp<GrSemaphore> semaphore, SkColorType colorType,
+                                     std::unique_ptr<GrSemaphore> semaphore, SkColorType colorType,
                                      SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
     GrContext* context = texture->getContext();
 
@@ -64,15 +66,15 @@
           std::move(semaphore), backendTexture));
 }
 
-GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(const SkImageInfo& info,
-                                                               GrTexture* texture,
-                                                               GrSurfaceOrigin origin,
-                                                               uint32_t owningContextID,
-                                                               sk_sp<GrSemaphore> semaphore,
-                                                               const GrBackendTexture& backendTex)
+GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(
+        const SkImageInfo& info,
+        GrTexture* texture,
+        GrSurfaceOrigin origin,
+        uint32_t owningContextID,
+        std::unique_ptr<GrSemaphore> semaphore,
+        const GrBackendTexture& backendTex)
         : INHERITED(info)
-        , fRefHelper(new RefHelper(texture, owningContextID))
-        , fSemaphore(std::move(semaphore))
+        , fRefHelper(new RefHelper(texture, owningContextID, std::move(semaphore)))
         , fBackendTexture(backendTex)
         , fSurfaceOrigin(origin) {}
 
@@ -111,6 +113,7 @@
     if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) {
         if (fRefHelper->fBorrowingContextID != context->priv().contextID()) {
             fBorrowingMutex.release();
+            SkDebugf("GrBackendTextureImageGenerator: Trying to use texture on two GrContexts!\n");
             return nullptr;
         } else {
             SkASSERT(fRefHelper->fBorrowingContextReleaseProc);
@@ -160,11 +163,11 @@
     // Must make copies of member variables to capture in the lambda since this image generator may
     // be deleted before we actually execute the lambda.
     sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
-            [refHelper = fRefHelper, releaseProcHelper, semaphore = fSemaphore,
-             backendTexture = fBackendTexture, grColorType](
+            [refHelper = fRefHelper, releaseProcHelper, backendTexture = fBackendTexture,
+             grColorType](
                     GrResourceProvider* resourceProvider) -> GrSurfaceProxy::LazyCallbackResult {
-                if (semaphore) {
-                    resourceProvider->priv().gpu()->waitSemaphore(semaphore);
+                if (refHelper->fSemaphore) {
+                    resourceProvider->priv().gpu()->waitSemaphore(refHelper->fSemaphore.get());
                 }
 
                 // If a client re-draws the same image multiple times, the texture we return
@@ -218,7 +221,7 @@
         GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
         SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
 
-        return GrSurfaceProxy::Copy(context, proxy.get(), grColorType, mipMapped, subset,
-                                    SkBackingFit::kExact, SkBudgeted::kYes);
+        return GrSurfaceProxy::Copy(context, proxy.get(), mipMapped, subset, SkBackingFit::kExact,
+                                    SkBudgeted::kYes);
     }
 }
diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h
index 8a82c5c..887ff21 100644
--- a/src/gpu/GrBackendTextureImageGenerator.h
+++ b/src/gpu/GrBackendTextureImageGenerator.h
@@ -29,7 +29,7 @@
 class GrBackendTextureImageGenerator : public SkImageGenerator {
 public:
     static std::unique_ptr<SkImageGenerator> Make(sk_sp<GrTexture>, GrSurfaceOrigin,
-                                                  sk_sp<GrSemaphore>, SkColorType,
+                                                  std::unique_ptr<GrSemaphore>, SkColorType,
                                                   SkAlphaType, sk_sp<SkColorSpace>);
 
     ~GrBackendTextureImageGenerator() override;
@@ -45,42 +45,42 @@
 
 private:
     GrBackendTextureImageGenerator(const SkImageInfo& info, GrTexture*, GrSurfaceOrigin,
-                                   uint32_t owningContextID, sk_sp<GrSemaphore>,
+                                   uint32_t owningContextID, std::unique_ptr<GrSemaphore>,
                                    const GrBackendTexture&);
 
     static void ReleaseRefHelper_TextureReleaseProc(void* ctx);
 
     class RefHelper : public SkNVRefCnt<RefHelper> {
     public:
-        RefHelper(GrTexture*, uint32_t owningContextID);
+        RefHelper(GrTexture*, uint32_t owningContextID, std::unique_ptr<GrSemaphore>);
 
         ~RefHelper();
 
-        GrTexture*          fOriginalTexture;
-        uint32_t            fOwningContextID;
+        GrTexture*                   fOriginalTexture;
+        uint32_t                     fOwningContextID;
 
         // We use this key so that we don't rewrap the GrBackendTexture in a GrTexture for each
         // proxy created from this generator for a particular borrowing context.
-        GrUniqueKey         fBorrowedTextureKey;
+        GrUniqueKey                  fBorrowedTextureKey;
         // There is no ref associated with this pointer. We rely on our atomic bookkeeping with the
         // context ID to know when this pointer is valid and safe to use. This is used to make sure
         // all uses of the wrapped texture are finished on the borrowing context before we open
         // this back up to other contexts. In general a ref to this release proc is owned by all
         // proxies and gpu uses of the backend texture.
-        GrRefCntedCallback*  fBorrowingContextReleaseProc;
-        uint32_t             fBorrowingContextID;
+        GrRefCntedCallback*          fBorrowingContextReleaseProc;
+        uint32_t                     fBorrowingContextID;
+
+        std::unique_ptr<GrSemaphore> fSemaphore;
     };
 
-    RefHelper*           fRefHelper;
+    RefHelper*       fRefHelper;
     // This Mutex is used to guard the borrowing of the texture to one GrContext at a time as well
     // as the creation of the fBorrowingContextReleaseProc. The latter happening if two threads with
     // the same consuming GrContext try to generate a texture at the same time.
-    SkMutex              fBorrowingMutex;
+    SkMutex          fBorrowingMutex;
 
-    sk_sp<GrSemaphore>   fSemaphore;
-
-    GrBackendTexture     fBackendTexture;
-    GrSurfaceOrigin      fSurfaceOrigin;
+    GrBackendTexture fBackendTexture;
+    GrSurfaceOrigin  fSurfaceOrigin;
 
     typedef SkImageGenerator INHERITED;
 };
diff --git a/src/gpu/GrBlurUtils.cpp b/src/gpu/GrBlurUtils.cpp
index eae8224..e3f9260 100644
--- a/src/gpu/GrBlurUtils.cpp
+++ b/src/gpu/GrBlurUtils.cpp
@@ -38,8 +38,7 @@
                       const SkMatrix& viewMatrix,
                       const SkIRect& maskRect,
                       GrPaint&& paint,
-                      sk_sp<GrTextureProxy> mask,
-                      GrColorType maskColorType) {
+                      sk_sp<GrTextureProxy> mask) {
     SkMatrix inverse;
     if (!viewMatrix.invert(&inverse)) {
         return false;
@@ -48,8 +47,8 @@
     SkMatrix matrix = SkMatrix::MakeTrans(-SkIntToScalar(maskRect.fLeft),
                                           -SkIntToScalar(maskRect.fTop));
     matrix.preConcat(viewMatrix);
-    paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(std::move(mask), maskColorType,
-                                                                   matrix));
+    paint.addCoverageFragmentProcessor(
+            GrSimpleTextureEffect::Make(std::move(mask), kUnknown_SkAlphaType, matrix));
 
     renderTargetContext->fillRectWithLocalMatrix(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
                                                  SkRect::Make(maskRect), inverse);
@@ -166,8 +165,8 @@
         }
     }
 
-    return draw_mask(renderTargetContext, clipData, viewMatrix, drawRect,
-                     std::move(paint), std::move(filteredMask), GrColorType::kAlpha_8);
+    return draw_mask(renderTargetContext, clipData, viewMatrix, drawRect, std::move(paint),
+                     std::move(filteredMask));
 }
 
 // Create a mask of 'shape' and return the resulting renderTargetContext
@@ -426,9 +425,8 @@
         }
 
         if (filteredMask) {
-            if (draw_mask(renderTargetContext, clip, viewMatrix,
-                          maskRect, std::move(paint), std::move(filteredMask),
-                          GrColorType::kAlpha_8)) {
+            if (draw_mask(renderTargetContext, clip, viewMatrix, maskRect, std::move(paint),
+                          std::move(filteredMask))) {
                 // This path is completely drawn
                 return;
             }
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index dd2aa36..48dc58c 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -40,6 +40,7 @@
     fDynamicStateArrayGeometryProcessorTextureSupport = false;
     fPerformPartialClearsAsDraws = false;
     fPerformColorClearsAsDraws = false;
+    fAvoidLargeIndexBufferDraws = false;
     fPerformStencilClearsAsDraws = false;
     fAllowCoverageCounting = false;
     fTransferBufferSupport = false;
@@ -80,7 +81,19 @@
     fDriverBugWorkarounds = options.fDriverBugWorkarounds;
 }
 
+void GrCaps::finishInitialization(const GrContextOptions& options) {
+    if (fMixedSamplesSupport) {
+        // We need multisample disable and dual source blending in order to support mixed samples.
+        fMixedSamplesSupport = this->multisampleDisableSupport() &&
+                               this->shaderCaps()->dualSourceBlendingSupport();
+    }
+
+    // Overrides happen last.
+    this->applyOptionsOverrides(options);
+}
+
 void GrCaps::applyOptionsOverrides(const GrContextOptions& options) {
+    fShaderCaps->applyOptionsOverrides(options);
     this->onApplyOptionsOverrides(options);
     if (options.fDisableDriverCorrectnessWorkarounds) {
         SkASSERT(!fDriverBlacklistCCPR);
@@ -109,8 +122,10 @@
     if (options.fMaxTileSizeOverride && options.fMaxTileSizeOverride < fMaxTextureSize) {
         fMaxTileSize = options.fMaxTileSizeOverride;
     }
-    if (options.fSuppressGeometryShaders) {
-        fShaderCaps->fGeometryShaderSupport = false;
+    if (options.fSuppressDualSourceBlending) {
+        // GrShaderCaps::applyOptionsOverrides already handled the rest; here we just need to make
+        // sure mixed samples gets disabled if dual source blending is suppressed.
+        fMixedSamplesSupport = false;
     }
     if (options.fClearAllTextures) {
         fShouldInitializeTextures = true;
@@ -189,6 +204,7 @@
                        fDynamicStateArrayGeometryProcessorTextureSupport);
     writer->appendBool("Use draws for partial clears", fPerformPartialClearsAsDraws);
     writer->appendBool("Use draws for color clears", fPerformColorClearsAsDraws);
+    writer->appendBool("Avoid Large IndexBuffer Draws", fAvoidLargeIndexBufferDraws);
     writer->appendBool("Use draws for stencil clip clears", fPerformStencilClearsAsDraws);
     writer->appendBool("Allow coverage counting shortcuts", fAllowCoverageCounting);
     writer->appendBool("Supports transfer buffers", fTransferBufferSupport);
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 27f0ec4..3c5501d 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -21,7 +21,11 @@
 class GrBackendRenderTarget;
 class GrBackendTexture;
 struct GrContextOptions;
+class GrProcessorKeyBuilder;
+class GrProgramDesc;
+class GrProgramInfo;
 class GrRenderTargetProxy;
+class GrSamplerState;
 class GrSurface;
 class SkJSONWriter;
 
@@ -330,6 +334,8 @@
     // Many drivers have issues with color clears.
     bool performColorClearsAsDraws() const { return fPerformColorClearsAsDraws; }
 
+    bool avoidLargeIndexBufferDraws() const { return fAvoidLargeIndexBufferDraws; }
+
     /// Adreno 4xx devices experience an issue when there are a large number of stencil clip bit
     /// clears. The minimal repro steps are not precisely known but drawing a rect with a stencil
     /// op instead of using glClear seems to resolve the issue.
@@ -430,6 +436,17 @@
         return result;
     }
 
+    /**
+     * Adds fields to the key to represent the sampler that will be created for the passed
+     * in parameters. Currently this extra keying is only needed when building a vulkan pipeline
+     * with immutable samplers.
+     */
+    virtual void addExtraSamplerKey(GrProcessorKeyBuilder*,
+                                    const GrSamplerState&,
+                                    const GrBackendFormat&) const {}
+
+    virtual GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const = 0;
+
 #ifdef SK_DEBUG
     // This is just a debugging entry point until we're weaned off of GrPixelConfig. It
     // should be used to verify that the pixel config from user-level code (the genericConfig)
@@ -447,10 +464,10 @@
 #endif
 
 protected:
-    /** Subclasses must call this at the end of their constructors in order to apply caps
-        overrides requested by the client. Note that overrides will only reduce the caps never
-        expand them. */
-    void applyOptionsOverrides(const GrContextOptions& options);
+    // Subclasses must call this at the end of their init method in order to do final processing on
+    // the caps (including overrides requested by the client).
+    // NOTE: this method will only reduce the caps, never expand them.
+    void finishInitialization(const GrContextOptions& options);
 
     sk_sp<GrShaderCaps> fShaderCaps;
 
@@ -476,6 +493,7 @@
     bool fClampToBorderSupport                       : 1;
     bool fPerformPartialClearsAsDraws                : 1;
     bool fPerformColorClearsAsDraws                  : 1;
+    bool fAvoidLargeIndexBufferDraws                 : 1;
     bool fPerformStencilClearsAsDraws                : 1;
     bool fAllowCoverageCounting                      : 1;
     bool fTransferBufferSupport                      : 1;
@@ -521,6 +539,8 @@
     GrDriverBugWorkarounds fDriverBugWorkarounds;
 
 private:
+    void applyOptionsOverrides(const GrContextOptions& options);
+
     virtual void onApplyOptionsOverrides(const GrContextOptions&) {}
     virtual void onDumpJSON(SkJSONWriter*) const {}
     virtual bool onSurfaceSupportsWritePixels(const GrSurface*) const = 0;
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index f099361..205a198 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -12,6 +12,7 @@
 #include "include/private/SkDeferredDisplayList.h"
 #include "include/private/SkImageInfoPriv.h"
 #include "src/core/SkMakeUnique.h"
+#include "src/core/SkMipMap.h"
 #include "src/core/SkTaskGroup.h"
 #include "src/gpu/GrClientMappedBufferManager.h"
 #include "src/gpu/GrContextPriv.h"
@@ -287,10 +288,10 @@
         return false;
     }
     for (int i = 0; i < numSemaphores; ++i) {
-        sk_sp<GrSemaphore> sema = fResourceProvider->wrapBackendSemaphore(
+        std::unique_ptr<GrSemaphore> sema = fResourceProvider->wrapBackendSemaphore(
                 waitSemaphores[i], GrResourceProvider::SemaphoreWrapType::kWillWait,
                 kAdopt_GrWrapOwnership);
-        fGpu->waitSemaphore(std::move(sema));
+        fGpu->waitSemaphore(sema.get());
     }
     return true;
 }
@@ -380,9 +381,13 @@
         return GrBackendTexture();
     }
 
-    return fGpu->createBackendTexture(width, height, backendFormat,
-                                      mipMapped, renderable,
-                                      nullptr, 0, nullptr, isProtected);
+    int numMipLevels = 1;
+    if (mipMapped == GrMipMapped::kYes) {
+        numMipLevels = SkMipMap::ComputeLevelCount(width, height) + 1;
+    }
+
+    return fGpu->createBackendTexture({width, height}, backendFormat, renderable, nullptr,
+                                      numMipLevels, isProtected);
 }
 
 GrBackendTexture GrContext::createBackendTexture(int width, int height,
@@ -481,9 +486,13 @@
         return GrBackendTexture();
     }
 
-    return fGpu->createBackendTexture(width, height, backendFormat,
-                                      mipMapped, renderable,
-                                      nullptr, 0, &color, isProtected);
+    int numMipLevels = 1;
+    if (mipMapped == GrMipMapped::kYes) {
+        numMipLevels = SkMipMap::ComputeLevelCount(width, height) + 1;
+    }
+    GrGpu::BackendTextureData data(color);
+    return fGpu->createBackendTexture({width, height}, backendFormat, renderable, &data,
+                                      numMipLevels, isProtected);
 }
 
 GrBackendTexture GrContext::createBackendTexture(int width, int height,
@@ -534,9 +543,9 @@
 
     GrBackendFormat backendFormat = this->defaultBackendFormat(colorType, renderable);
 
-    return fGpu->createBackendTexture(baseWidth, baseHeight, backendFormat,
-                                      numLevels > 1 ? GrMipMapped::kYes : GrMipMapped::kNo,
-                                      renderable, srcData, numLevels, nullptr, isProtected);
+    GrGpu::BackendTextureData data(srcData);
+    return fGpu->createBackendTexture({baseWidth, baseHeight}, backendFormat, renderable, &data,
+                                      numLevels, isProtected);
 }
 
 void GrContext::deleteBackendTexture(GrBackendTexture backendTex) {
diff --git a/src/gpu/GrCoordTransform.h b/src/gpu/GrCoordTransform.h
index d592c21..ba85fb1 100644
--- a/src/gpu/GrCoordTransform.h
+++ b/src/gpu/GrCoordTransform.h
@@ -20,121 +20,70 @@
  */
 class GrCoordTransform {
 public:
-    GrCoordTransform()
-            : fProxy(nullptr)
-            , fNormalize(false)
-            , fReverseY(false)
-            , fComputeInVertexShader(true) {
-        SkDEBUGCODE(fInProcessor = false);
-    }
+    GrCoordTransform() = default;
 
     GrCoordTransform(const GrCoordTransform&) = default;
 
     /**
-     * Create a transformation that maps [0, 1] to a proxy's boundaries. The proxy origin also
-     * implies whether a y-reversal should be performed.
+     * Create a transformation that maps [0, proxy->width()] x [0, proxy->height()] to a proxy's
+     * extent.
      */
-    GrCoordTransform(GrTextureProxy* proxy) {
-        SkASSERT(proxy);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(SkMatrix::I(), proxy);
-    }
+    GrCoordTransform(GrSurfaceProxy* proxy) : fProxy(proxy) {}
 
     /**
      * Create a transformation from a matrix. The proxy origin also implies whether a y-reversal
      * should be performed.
      */
-    GrCoordTransform(const SkMatrix& m, GrTextureProxy* proxy) {
+    GrCoordTransform(const SkMatrix& m, GrSurfaceProxy* proxy) : fProxy(proxy), fMatrix(m) {
         SkASSERT(proxy);
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(m, proxy);
     }
 
     /**
      * Create a transformation that applies the matrix to a coord set.
      */
-    GrCoordTransform(const SkMatrix& m) {
-        SkDEBUGCODE(fInProcessor = false);
-        this->reset(m);
-    }
+    GrCoordTransform(const SkMatrix& m) : fMatrix(m) {}
 
-    GrCoordTransform& operator= (const GrCoordTransform& that) {
-        SkASSERT(!fInProcessor);
-        fMatrix = that.fMatrix;
-        fProxy = that.fProxy;
-        fNormalize = that.fNormalize;
-        fReverseY = that.fReverseY;
-        return *this;
-    }
+    GrCoordTransform& operator=(const GrCoordTransform& that) = default;
 
-    /**
-     * Access the matrix for editing. Note, this must be done before adding the transform to an
-     * effect, since effects are immutable.
-     */
-    SkMatrix* accessMatrix() {
-        SkASSERT(!fInProcessor);
-        return &fMatrix;
-    }
+    // The textures' effect is to optionally normalize the final matrix, so a blind equality check
+    // could be misleading.
+    bool operator==(const GrCoordTransform& that) const = delete;
+    bool operator!=(const GrCoordTransform& that) const = delete;
 
-    bool hasSameEffectAs(const GrCoordTransform& that) const {
-        if (fNormalize != that.fNormalize ||
-            fReverseY != that.fReverseY ||
-            !fMatrix.cheapEqualTo(that.fMatrix)) {
+    bool hasSameEffectiveMatrix(const GrCoordTransform& that) const {
+        // This is slightly more conservative than computing each transforms effective matrix and
+        // then comparing them.
+        if (!fMatrix.cheapEqualTo(that.fMatrix)) {
             return false;
         }
-
-        if (fNormalize) {
-            if (fProxy->backingStoreDimensions() != that.fProxy->backingStoreDimensions()) {
-                return false;
-            }
+        if (SkToBool(fProxy) != SkToBool(that.fProxy)) {
+            return false;
         }
-
+        if (this->normalize() != that.normalize() || this->reverseY() != that.reverseY()) {
+            return false;
+        }
+        if (this->normalize() &&
+            fProxy->backingStoreDimensions() != that.fProxy->backingStoreDimensions()) {
+            return false;
+        }
         return true;
     }
 
-    const SkMatrix& getMatrix() const { return fMatrix; }
-    const GrTextureProxy* proxy() const { return fProxy; }
-    bool normalize() const { return fNormalize; }
-    bool reverseY() const { return fReverseY; }
+    const SkMatrix& matrix() const { return fMatrix; }
+    const GrSurfaceProxy* proxy() const { return fProxy; }
+    bool normalize() const {
+        return fProxy && fProxy->backendFormat().textureType() != GrTextureType::kRectangle;
+    }
+    bool reverseY() const { return fProxy && fProxy->origin() == kBottomLeft_GrSurfaceOrigin; }
+    bool isNoOp() const { return fMatrix.isIdentity() && !this->normalize() && !this->reverseY(); }
 
     // This should only ever be called at flush time after the backing texture has been
     // successfully instantiated
     GrTexture* peekTexture() const { return fProxy->peekTexture(); }
 
-    bool computeInVertexShader() const { return fComputeInVertexShader; }
-
-    void setComputeInVertexShader(bool computeInVertexShader) {
-        fComputeInVertexShader = computeInVertexShader;
-    }
-
 private:
-    void reset(const SkMatrix& m, GrTextureProxy* proxy = nullptr) {
-        SkASSERT(!fInProcessor);
-
-        fMatrix = m;
-        fProxy = proxy;
-        fNormalize = proxy && proxy->textureType() != GrTextureType::kRectangle;
-        fReverseY = proxy && kBottomLeft_GrSurfaceOrigin == proxy->origin();
-        fComputeInVertexShader = true;
-    }
-
-    // The textures' effect is to optionally normalize the final matrix, so a blind
-    // equality check could be misleading
-    bool operator==(const GrCoordTransform& that) const;
-    bool operator!=(const GrCoordTransform& that) const;
-
-    SkMatrix                fMatrix;
-    const GrTextureProxy*   fProxy;
-    bool                    fNormalize;
-    bool                    fReverseY;
-    bool                    fComputeInVertexShader;
-
-#ifdef SK_DEBUG
-public:
-    void setInProcessor() const { fInProcessor = true; }
-private:
-    mutable bool fInProcessor;
-#endif
+    const GrSurfaceProxy* fProxy = nullptr;
+    SkMatrix fMatrix = SkMatrix::I();
 };
 
 #endif
diff --git a/src/gpu/GrCopyRenderTask.cpp b/src/gpu/GrCopyRenderTask.cpp
index 9e44187..39fa454 100644
--- a/src/gpu/GrCopyRenderTask.cpp
+++ b/src/gpu/GrCopyRenderTask.cpp
@@ -11,15 +11,17 @@
 #include "src/gpu/GrOpFlushState.h"
 #include "src/gpu/GrResourceAllocator.h"
 
-sk_sp<GrRenderTask> GrCopyRenderTask::Make(sk_sp<GrSurfaceProxy> srcProxy,
+sk_sp<GrRenderTask> GrCopyRenderTask::Make(GrSurfaceProxyView srcView,
                                            const SkIRect& srcRect,
-                                           sk_sp<GrSurfaceProxy> dstProxy,
+                                           GrSurfaceProxyView dstView,
                                            const SkIPoint& dstPoint,
                                            const GrCaps* caps) {
-    SkASSERT(dstProxy);
-    SkASSERT(srcProxy);
+    SkASSERT(dstView.proxy());
+    SkASSERT(srcView.proxy());
     SkIRect clippedSrcRect;
     SkIPoint clippedDstPoint;
+    GrSurfaceProxy* srcProxy = srcView.proxy();
+    GrSurfaceProxy* dstProxy = dstView.proxy();
     // If the rect is outside the srcProxy or dstProxy then we've already succeeded.
     if (!GrClipSrcRectAndDstPoint(dstProxy->dimensions(), srcProxy->dimensions(), srcRect, dstPoint,
                                   &clippedSrcRect, &clippedDstPoint)) {
@@ -30,8 +32,8 @@
         return nullptr;
     }
 
-    SkASSERT(dstProxy->origin() == srcProxy->origin());
-    if (srcProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
+    SkASSERT(dstView.origin() == srcView.origin());
+    if (srcView.origin() == kBottomLeft_GrSurfaceOrigin) {
         int rectHeight = clippedSrcRect.height();
         clippedSrcRect.fTop = srcProxy->height() - clippedSrcRect.fBottom;
         clippedSrcRect.fBottom = clippedSrcRect.fTop + rectHeight;
@@ -39,44 +41,46 @@
     }
 
     sk_sp<GrCopyRenderTask> task(new GrCopyRenderTask(
-            std::move(srcProxy), clippedSrcRect, std::move(dstProxy), clippedDstPoint));
+            std::move(srcView), clippedSrcRect, std::move(dstView), clippedDstPoint));
     return task;
 }
 
-GrCopyRenderTask::GrCopyRenderTask(sk_sp<GrSurfaceProxy> srcProxy,
+GrCopyRenderTask::GrCopyRenderTask(GrSurfaceProxyView srcView,
                                    const SkIRect& srcRect,
-                                   sk_sp<GrSurfaceProxy> dstProxy,
+                                   GrSurfaceProxyView dstView,
                                    const SkIPoint& dstPoint)
-        : GrRenderTask(std::move(dstProxy))
-        , fSrcProxy(std::move(srcProxy))
+        : GrRenderTask(std::move(dstView))
+        , fSrcView(std::move(srcView))
         , fSrcRect(srcRect)
         , fDstPoint(dstPoint) {
-    fTarget->setLastRenderTask(this);
+    fTargetView.proxy()->setLastRenderTask(this);
 }
 
 void GrCopyRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
     // This renderTask doesn't have "normal" ops. In this case we still need to add an interval (so
     // fEndOfOpsTaskOpIndices will remain in sync), so we create a fake op# to capture the fact that
-    // we read fSrcProxy and copy to fTarget.
-    alloc->addInterval(fSrcProxy.get(), alloc->curOp(), alloc->curOp(),
+    // we read fSrcView and copy to fTargetView.
+    alloc->addInterval(fSrcView.proxy(), alloc->curOp(), alloc->curOp(),
                        GrResourceAllocator::ActualUse::kYes);
-    alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
+    alloc->addInterval(fTargetView.proxy(), alloc->curOp(), alloc->curOp(),
                        GrResourceAllocator::ActualUse::kYes);
     alloc->incOps();
 }
 
 bool GrCopyRenderTask::onExecute(GrOpFlushState* flushState) {
-    if (!fSrcProxy->isInstantiated() || !fTarget->isInstantiated()) {
+    GrSurfaceProxy* dstProxy = fTargetView.proxy();
+    GrSurfaceProxy* srcProxy = fSrcView.proxy();
+    if (!srcProxy->isInstantiated() || !dstProxy->isInstantiated()) {
         return false;
     }
-    GrSurface* srcSurface = fSrcProxy->peekSurface();
-    GrSurface* dstSurface = fTarget->peekSurface();
-    if (fSrcProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
-        if (fSrcProxy->height() != srcSurface->height()) {
-            fSrcRect.offset(0, srcSurface->height() - fSrcProxy->height());
+    GrSurface* srcSurface = srcProxy->peekSurface();
+    GrSurface* dstSurface = dstProxy->peekSurface();
+    if (fSrcView.origin() == kBottomLeft_GrSurfaceOrigin) {
+        if (srcProxy->height() != srcSurface->height()) {
+            fSrcRect.offset(0, srcSurface->height() - srcProxy->height());
         }
-        if (fTarget->height() != dstSurface->height()) {
-            fDstPoint.fY = fDstPoint.fY + (dstSurface->height() - fTarget->height());
+        if (dstProxy->height() != dstSurface->height()) {
+            fDstPoint.fY = fDstPoint.fY + (dstSurface->height() - dstProxy->height());
         }
     }
     return flushState->gpu()->copySurface(dstSurface, srcSurface, fSrcRect, fDstPoint);
diff --git a/src/gpu/GrCopyRenderTask.h b/src/gpu/GrCopyRenderTask.h
index 640bb11..7577ffc 100644
--- a/src/gpu/GrCopyRenderTask.h
+++ b/src/gpu/GrCopyRenderTask.h
@@ -12,21 +12,22 @@
 
 class GrCopyRenderTask final : public GrRenderTask {
 public:
-    static sk_sp<GrRenderTask> Make(sk_sp<GrSurfaceProxy> srcProxy,
+    static sk_sp<GrRenderTask> Make(GrSurfaceProxyView srcView,
                                     const SkIRect& srcRect,
-                                    sk_sp<GrSurfaceProxy> dstProxy,
+                                    GrSurfaceProxyView dstView,
                                     const SkIPoint& dstPoint,
                                     const GrCaps*);
 
 private:
-    GrCopyRenderTask(sk_sp<GrSurfaceProxy> srcProxy,
+    GrCopyRenderTask(GrSurfaceProxyView srcView,
                      const SkIRect& srcRect,
-                     sk_sp<GrSurfaceProxy> dstProxy,
+                     GrSurfaceProxyView dstView,
                      const SkIPoint& dstPoint);
 
     bool onIsUsed(GrSurfaceProxy* proxy) const override {
-        SkASSERT(proxy != fTarget.get());  // This case should be handled by GrRenderTask.
-        return proxy == fSrcProxy.get();
+        // This case should be handled by GrRenderTask.
+        SkASSERT(proxy != fTargetView.proxy());
+        return proxy == fSrcView.proxy();
     }
     // If instantiation failed, at flush time we simply will skip doing the copy.
     void handleInternalAllocationFailure() override {}
@@ -39,12 +40,12 @@
     bool onExecute(GrOpFlushState*) override;
 
 #ifdef SK_DEBUG
-    void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {
-        fn(fSrcProxy.get(), GrMipMapped::kNo);
+    void visitProxies_debugOnly(const GrOp::VisitProxyFunc& fn) const override {
+        fn(fSrcView.proxy(), GrMipMapped::kNo);
     }
 #endif
 
-    sk_sp<GrSurfaceProxy> fSrcProxy;
+    GrSurfaceProxyView fSrcView;
     SkIRect fSrcRect;
     SkIPoint fDstPoint;
 };
diff --git a/src/gpu/GrDDLContext.cpp b/src/gpu/GrDDLContext.cpp
index 4d14504..1e4a17f 100644
--- a/src/gpu/GrDDLContext.cpp
+++ b/src/gpu/GrDDLContext.cpp
@@ -15,14 +15,14 @@
  * The DDL Context is the one in effect during DDL Recording. It isn't backed by a GrGPU and
  * cannot allocate any GPU resources.
  */
-class GrDDLContext : public GrContext {
+class GrDDLContext final : public GrContext {
 public:
     GrDDLContext(sk_sp<GrContextThreadSafeProxy> proxy)
             : INHERITED(proxy->backend(), proxy->priv().options(), proxy->priv().contextID()) {
         fThreadSafeProxy = std::move(proxy);
     }
 
-    ~GrDDLContext() override { }
+    ~GrDDLContext() override {}
 
     void abandonContext() override {
         SkASSERT(0); // abandoning in a DDL Recorder doesn't make a whole lot of sense
@@ -39,7 +39,7 @@
         INHERITED::freeGpuResources();
     }
 
-protected:
+private:
     // TODO: Here we're pretending this isn't derived from GrContext. Switch this to be derived from
     // GrRecordingContext!
     GrContext* asDirectContext() override { return nullptr; }
@@ -66,7 +66,6 @@
         return nullptr;
     }
 
-private:
     typedef GrContext INHERITED;
 };
 
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
index 6265aae..b820b7d 100644
--- a/src/gpu/GrDataUtils.cpp
+++ b/src/gpu/GrDataUtils.cpp
@@ -153,151 +153,15 @@
     }
 }
 
-// Fill in the width x height 'dest' with the munged version of 'colorf' that matches 'config'
-static bool fill_buffer_with_color(GrColorType colorType, int width, int height,
-                                   const SkColor4f& colorf, void* dest) {
-    GrColor color = colorf.toBytes_RGBA();
-
-    uint8_t r = GrColorUnpackR(color);
-    uint8_t g = GrColorUnpackG(color);
-    uint8_t b = GrColorUnpackB(color);
-    uint8_t a = GrColorUnpackA(color);
-
-    switch (colorType) {
-        case GrColorType::kAlpha_8: {
-            memset(dest, a, width * height);
-            break;
-        }
-        case GrColorType::kGray_8: {
-            uint8_t gray8 = SkComputeLuminance(r, g, b);
-
-            memset(dest, gray8, width * height);
-            break;
-        }
-        case GrColorType::kBGR_565: {
-            uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
-
-            sk_memset16((uint16_t*) dest, rgb565, width * height);
-            break;
-        }
-        case GrColorType::kABGR_4444: {
-            uint8_t r4 = (r >> 4) & 0xF;
-            uint8_t g4 = (g >> 4) & 0xF;
-            uint8_t b4 = (b >> 4) & 0xF;
-            uint8_t a4 = (a >> 4) & 0xF;
-
-            uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
-                                b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
-
-            sk_memset16((uint16_t*) dest, rgba4444, width * height);
-            break;
-        }
-        case GrColorType::kRGBA_8888: {
-            sk_memset32((uint32_t *) dest, color, width * height);
-            break;
-        }
-        case GrColorType::kRGB_888x: {
-            GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
-
-            sk_memset32((uint32_t *) dest, opaque, width * height);
-            break;
-        }
-        case GrColorType::kRG_88: {
-            uint16_t rg88 = (g << 8) | r;
-
-            sk_memset16((uint16_t*) dest, rg88, width * height);
-            break;
-        }
-        case GrColorType::kBGRA_8888: {
-            GrColor swizzled = GrColorPackRGBA(b, g, r, a);
-
-            sk_memset32((uint32_t *) dest, swizzled, width * height);
-            break;
-        }
-        case GrColorType::kRGBA_8888_SRGB: {
-            sk_memset32((uint32_t *) dest, color, width * height);
-            break;
-        }
-        case GrColorType::kRGBA_1010102: {
-            uint32_t r10 = SkScalarRoundToInt(colorf.fR * 1023.0f);
-            uint32_t g10 = SkScalarRoundToInt(colorf.fG * 1023.0f);
-            uint32_t b10 = SkScalarRoundToInt(colorf.fB * 1023.0f);
-            uint8_t  a2  = SkScalarRoundToInt(colorf.fA * 3.0f);
-
-            uint32_t rgba1010102 = a2 << 30 | b10 << 20 | g10 << 10 | r10;
-
-            sk_memset32((uint32_t *) dest, rgba1010102, width * height);
-            break;
-        }
-        case GrColorType::kAlpha_F16: {
-            SkHalf alphaHalf = SkFloatToHalf(colorf.fA);
-
-            sk_memset16((uint16_t *) dest, alphaHalf, width * height);
-            break;
-        }
-        case GrColorType::kRGBA_F16_Clamped:                          // fall through
-        case GrColorType::kRGBA_F16: {
-            uint64_t rHalf = SkFloatToHalf(colorf.fR);
-            uint64_t gHalf = SkFloatToHalf(colorf.fG);
-            uint64_t bHalf = SkFloatToHalf(colorf.fB);
-            uint64_t aHalf = SkFloatToHalf(colorf.fA);
-
-            uint64_t rgbaHalf = (aHalf << 48) | (bHalf << 32) | (gHalf << 16) | rHalf;
-
-            sk_memset64((uint64_t *) dest, rgbaHalf, width * height);
-            break;
-        }
-        case GrColorType::kAlpha_16: {
-            uint16_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
-            sk_memset16((uint16_t*) dest, a16, width * height);
-            break;
-        }
-        case GrColorType::kRG_1616: {
-            uint32_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
-            uint32_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
-
-            uint32_t rg1616 = (g16 << 16) | r16;
-
-            sk_memset32((uint32_t*) dest, rg1616, width * height);
-            break;
-        }
-        case GrColorType::kRGBA_16161616: {
-            uint64_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
-            uint64_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
-            uint64_t b16 = SkScalarRoundToInt(colorf.fB * 65535.0f);
-            uint64_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
-
-            uint64_t rgba16161616 = (a16 << 48) | (b16 << 32) | (g16 << 16) | r16;
-            sk_memset64((uint64_t*) dest, rgba16161616, width * height);
-            break;
-        }
-        case GrColorType::kRG_F16: {
-            uint32_t rHalf = SkFloatToHalf(colorf.fR);
-            uint32_t gHalf = SkFloatToHalf(colorf.fG);
-
-            uint32_t rgHalf = (gHalf << 16) | rHalf;
-
-            sk_memset32((uint32_t *) dest, rgHalf, width * height);
-            break;
-        }
-        default:
-            return false;
-            break;
-    }
-
-    return true;
-}
-
-size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, int baseWidth, int baseHeight,
+size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, SkISize baseDimensions,
                                         SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
     SkASSERT(individualMipOffsets && !individualMipOffsets->count());
     SkASSERT(mipLevelCount >= 1);
 
     individualMipOffsets->push_back(0);
 
-    size_t combinedBufferSize = baseWidth * bytesPerPixel * baseHeight;
-    int currentWidth = baseWidth;
-    int currentHeight = baseHeight;
+    size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height();
+    SkISize levelDimensions = baseDimensions;
 
     // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
     // least 4 bytes and a multiple of the bytes per pixel of the image config.
@@ -306,10 +170,10 @@
     int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
 
     for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
-        currentWidth = SkTMax(1, currentWidth / 2);
-        currentHeight = SkTMax(1, currentHeight / 2);
+        levelDimensions = {SkTMax(1, levelDimensions.width() /2),
+                           SkTMax(1, levelDimensions.height()/2)};
 
-        size_t trimmedSize = currentWidth * bytesPerPixel * currentHeight;
+        size_t trimmedSize = levelDimensions.area() * bytesPerPixel;
         const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
         if (alignmentDiff != 0) {
             combinedBufferSize += desiredAlignment - alignmentDiff;
@@ -324,24 +188,6 @@
     return combinedBufferSize;
 }
 
-void GrFillInData(GrColorType colorType, int baseWidth, int baseHeight,
-                  const SkTArray<size_t>& individualMipOffsets, char* dstPixels,
-                  const SkColor4f& colorf) {
-    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
-    int mipLevels = individualMipOffsets.count();
-
-    int currentWidth = baseWidth;
-    int currentHeight = baseHeight;
-    for (int currentMipLevel = 0; currentMipLevel < mipLevels; ++currentMipLevel) {
-        size_t offset = individualMipOffsets[currentMipLevel];
-
-        fill_buffer_with_color(colorType, currentWidth, currentHeight, colorf,
-                               &(dstPixels[offset]));
-        currentWidth = SkTMax(1, currentWidth / 2);
-        currentHeight = SkTMax(1, currentHeight / 2);
-    }
-}
-
 void GrFillInCompressedData(SkImage::CompressionType type, int baseWidth, int baseHeight,
                             char* dstPixels, const SkColor4f& colorf) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
@@ -352,7 +198,7 @@
     }
 }
 
-static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
+static GrSwizzle get_load_and_src_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
                                           bool* isNormalized, bool* isSRGB) {
     GrSwizzle swizzle("rgba");
     *isNormalized = true;
@@ -401,6 +247,12 @@
                                              swizzle = GrSwizzle("rgb1");
                                              break;
 
+        // These are color types we don't expect to ever have to load.
+        case GrColorType::kRGB_888:
+        case GrColorType::kR_8:
+        case GrColorType::kR_16:
+        case GrColorType::kR_F16:
+        case GrColorType::kGray_F16:
         case GrColorType::kUnknown:
             SK_ABORT("unexpected CT");
     }
@@ -408,10 +260,11 @@
 }
 
 static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
-                                           bool* isNormalized, bool* isSRGB) {
+                                           bool* doLumToAlpha, bool* isNormalized, bool* isSRGB) {
     GrSwizzle swizzle("rgba");
     *isNormalized = true;
     *isSRGB = false;
+    *doLumToAlpha = false;
     switch (ct) {
         case GrColorType::kAlpha_8:          *store = SkRasterPipeline::store_a8;       break;
         case GrColorType::kAlpha_16:         *store = SkRasterPipeline::store_a16;      break;
@@ -451,9 +304,28 @@
         case GrColorType::kRGB_888x:         swizzle = GrSwizzle("rgb1");
                                              *store = SkRasterPipeline::store_8888;
                                              break;
+        case GrColorType::kR_8:              swizzle = GrSwizzle("agbr");
+                                             *store = SkRasterPipeline::store_a8;
+                                             break;
+        case GrColorType::kR_16:             swizzle = GrSwizzle("agbr");
+                                             *store = SkRasterPipeline::store_a16;
+                                             break;
+        case GrColorType::kR_F16:            swizzle = GrSwizzle("agbr");
+                                             *store = SkRasterPipeline::store_af16;
+                                             break;
+        case GrColorType::kGray_F16:         *doLumToAlpha = true;
+                                             *store = SkRasterPipeline::store_af16;
+                                             break;
+        case GrColorType::kGray_8:           *doLumToAlpha = true;
+                                             *store = SkRasterPipeline::store_a8;
+                                             break;
+        case GrColorType::kGray_8xxx:        *doLumToAlpha = true;
+                                             *store = SkRasterPipeline::store_8888;
+                                             swizzle = GrSwizzle("a000");
+                                             break;
 
-        case GrColorType::kGray_8:  // not currently supported as output
-        case GrColorType::kGray_8xxx:  // not currently supported as output
+        // These are color types we don't expect to ever have to store.
+        case GrColorType::kRGB_888:  // This is handled specially in GrConvertPixels.
         case GrColorType::kUnknown:
             SK_ABORT("unexpected CT");
     }
@@ -471,22 +343,42 @@
                      const GrImageInfo& srcInfo, const void* src, size_t srcRB,
                      bool flipY) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
+    if (srcInfo.colorType() == GrColorType::kRGB_888) {
+        // We don't expect to have to convert from this format.
+        return false;
+    }
     if (!srcInfo.isValid() || !dstInfo.isValid()) {
         return false;
     }
     if (!src || !dst) {
         return false;
     }
-    if (dstInfo.width() != srcInfo.width() || srcInfo.height() != dstInfo.height()) {
-        return false;
-    }
-    if (GrColorTypeComponentFlags(dstInfo.colorType()) & kGray_SkColorTypeComponentFlag) {
-        // We don't currently support conversion to Gray.
+    if (dstInfo.dimensions() != srcInfo.dimensions()) {
         return false;
     }
     if (dstRB < dstInfo.minRowBytes() || srcRB < srcInfo.minRowBytes()) {
         return false;
     }
+    if (dstInfo.colorType() == GrColorType::kRGB_888) {
+        // SkRasterPipeline doesn't handle writing to RGB_888. So we have it write to RGB_888x and
+        // then do another conversion that does the 24bit packing.
+        auto tempDstInfo = dstInfo.makeColorType(GrColorType::kRGB_888x);
+        auto tempRB = tempDstInfo.minRowBytes();
+        std::unique_ptr<char[]> tempDst(new char[tempRB * tempDstInfo.height()]);
+        if (!GrConvertPixels(tempDstInfo, tempDst.get(), tempRB, srcInfo, src, srcRB, flipY)) {
+            return false;
+        }
+        auto* tRow = reinterpret_cast<const char*>(tempDst.get());
+        auto* dRow = reinterpret_cast<char*>(dst);
+        for (int y = 0; y < dstInfo.height(); ++y, tRow += tempRB, dRow += dstRB) {
+            for (int x = 0; x < dstInfo.width(); ++x) {
+                auto t = reinterpret_cast<const uint32_t*>(tRow + x * sizeof(uint32_t));
+                auto d = reinterpret_cast<uint32_t*>(dRow + x * 3);
+                memcpy(d, t, 3);
+            }
+        }
+        return true;
+    }
 
     size_t srcBpp = srcInfo.bpp();
     size_t dstBpp = dstInfo.bpp();
@@ -520,14 +412,15 @@
     SkRasterPipeline::StockStage load;
     bool srcIsNormalized;
     bool srcIsSRGB;
-    auto loadSwizzle = get_load_and_get_swizzle(srcInfo.colorType(), &load, &srcIsNormalized,
-                                                &srcIsSRGB);
+    auto loadSwizzle =
+            get_load_and_src_swizzle(srcInfo.colorType(), &load, &srcIsNormalized, &srcIsSRGB);
 
     SkRasterPipeline::StockStage store;
+    bool doLumToAlpha;
     bool dstIsNormalized;
     bool dstIsSRGB;
-    auto storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &dstIsNormalized,
-                                                  &dstIsSRGB);
+    auto storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &doLumToAlpha,
+                                                  &dstIsNormalized, &dstIsSRGB);
 
     bool clampGamut;
     SkTLazy<SkColorSpaceXformSteps> steps;
@@ -558,7 +451,7 @@
         std::swap(cnt, height);
     }
 
-    bool hasConversion = alphaOrCSConversion || clampGamut;
+    bool hasConversion = alphaOrCSConversion || clampGamut || doLumToAlpha;
 
     if (srcIsSRGB && dstIsSRGB && !hasConversion) {
         // No need to convert from srgb if we are just going to immediately convert it back.
@@ -581,10 +474,15 @@
             if (clampGamut) {
                 append_clamp_gamut(&pipeline);
             }
-            // If we add support for storing to Gray we would add a luminance to alpha conversion
-            // here. We also wouldn't then need a to_srgb stage after since it would have not effect
-            // on the alpha channel. It would also mean we have an SRGB Gray color type which
-            // doesn't exist currently.
+            if (doLumToAlpha) {
+                pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
+                // If we ever needed to convert from linear-encoded gray to sRGB-encoded
+                // gray we'd have a problem here because the subsequent to_srgb stage
+                // ignores the alpha channel (where we just stashed the gray). There are
+                // several ways that could be fixed but given our current set of color types
+                // this should never happen.
+                SkASSERT(!dstIsSRGB);
+            }
             if (dstIsSRGB) {
                 pipeline.append(SkRasterPipeline::to_srgb);
             }
@@ -600,6 +498,60 @@
     return true;
 }
 
+bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, SkColor4f color) {
+    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
+
+    if (!dstInfo.isValid()) {
+        return false;
+    }
+    if (!dst) {
+        return false;
+    }
+    if (dstRB < dstInfo.minRowBytes()) {
+        return false;
+    }
+    if (dstInfo.colorType() == GrColorType::kRGB_888) {
+        // SkRasterPipeline doesn't handle writing to RGB_888. So we handle that specially here.
+        uint32_t rgba = color.toBytes_RGBA();
+        for (int y = 0; y < dstInfo.height(); ++y) {
+            char* d = static_cast<char*>(dst) + y * dstRB;
+            for (int x = 0; x < dstInfo.width(); ++x, d += 3) {
+                memcpy(d, &rgba, 3);
+            }
+        }
+        return true;
+    }
+
+    bool doLumToAlpha;
+    bool isNormalized;
+    bool dstIsSRGB;
+    SkRasterPipeline::StockStage store;
+    GrSwizzle storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &doLumToAlpha,
+                                                       &isNormalized, &dstIsSRGB);
+    char block[64];
+    SkArenaAlloc alloc(block, sizeof(block), 1024);
+    SkRasterPipeline_<256> pipeline;
+    pipeline.append_constant_color(&alloc, color);
+    if (doLumToAlpha) {
+        pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
+        // If we ever needed to convert from linear-encoded gray to sRGB-encoded
+        // gray we'd have a problem here because the subsequent to_srgb stage
+        // ignores the alpha channel (where we just stashed the gray). There are
+        // several ways that could be fixed but given our current set of color types
+        // this should never happen.
+        SkASSERT(!dstIsSRGB);
+    }
+    if (dstIsSRGB) {
+        pipeline.append(SkRasterPipeline::to_srgb);
+    }
+    storeSwizzle.apply(&pipeline);
+    SkRasterPipeline_MemoryCtx dstCtx{dst, SkToInt(dstRB/dstInfo.bpp())};
+    pipeline.append(store, &dstCtx);
+    pipeline.run(0, 0, dstInfo.width(), dstInfo.height());
+
+    return true;
+}
+
 GrColorType SkColorTypeAndFormatToGrColorType(const GrCaps* caps,
                                               SkColorType skCT,
                                               const GrBackendFormat& format) {
diff --git a/src/gpu/GrDataUtils.h b/src/gpu/GrDataUtils.h
index b51a4dd..efdb302 100644
--- a/src/gpu/GrDataUtils.h
+++ b/src/gpu/GrDataUtils.h
@@ -23,12 +23,9 @@
 // Compute the size of the buffer required to hold all the mipLevels of the specified type
 // of data when all rowBytes are tight.
 // Note there may still be padding between the mipLevels to meet alignment requirements.
-size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, int baseWidth, int baseHeight,
+size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, SkISize baseDimensions,
                                         SkTArray<size_t>* individualMipOffsets, int mipLevelCount);
 
-void GrFillInData(GrColorType, int baseWidth, int baseHeight,
-                  const SkTArray<size_t>& individualMipOffsets, char* dest, const SkColor4f& color);
-
 void GrFillInCompressedData(SkImage::CompressionType, int width, int height, char* dest,
                             const SkColor4f& color);
 
@@ -37,4 +34,7 @@
                      const GrImageInfo& srcInfo, const void* src, size_t srcRB,
                      bool flipY = false);
 
+/** Clears the dst image to a constant color. */
+bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, SkColor4f color);
+
 #endif
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 09a6846..1000bd1 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/GrDefaultGeoProcFactory.h"
 
 #include "include/core/SkRefCnt.h"
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
@@ -34,17 +35,18 @@
 
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps* shaderCaps,
-                                           uint32_t gpTypeFlags,
-                                           const SkPMColor4f& color,
-                                           sk_sp<GrColorSpaceXform> colorSpaceXform,
-                                           const SkMatrix& viewMatrix,
-                                           const SkMatrix& localMatrix,
-                                           bool localCoordsWillBeRead,
-                                           uint8_t coverage) {
-        return sk_sp<GrGeometryProcessor>(new DefaultGeoProc(
-                shaderCaps, gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix,
-                coverage, localCoordsWillBeRead));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps* shaderCaps,
+                                     uint32_t gpTypeFlags,
+                                     const SkPMColor4f& color,
+                                     sk_sp<GrColorSpaceXform> colorSpaceXform,
+                                     const SkMatrix& viewMatrix,
+                                     const SkMatrix& localMatrix,
+                                     bool localCoordsWillBeRead,
+                                     uint8_t coverage) {
+        return arena->make<DefaultGeoProc>(shaderCaps, gpTypeFlags, color,
+                                           std::move(colorSpaceXform), viewMatrix, localMatrix,
+                                           coverage, localCoordsWillBeRead);
     }
 
     const char* name() const override { return "DefaultGeometryProcessor"; }
@@ -179,7 +181,7 @@
 
         void setData(const GrGLSLProgramDataManager& pdman,
                      const GrPrimitiveProcessor& gp,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             const DefaultGeoProc& dgp = gp.cast<DefaultGeoProc>();
 
             if (!dgp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dgp.viewMatrix())) {
@@ -198,7 +200,7 @@
                 pdman.set1f(fCoverageUniform, GrNormalizeByteToFloat(dgp.coverage()));
                 fCoverage = dgp.coverage();
             }
-            this->setTransformDataHelper(dgp.fLocalMatrix, pdman, &transformIter);
+            this->setTransformDataHelper(dgp.fLocalMatrix, pdman, transformRange);
 
             fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
         }
@@ -224,6 +226,8 @@
     }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     DefaultGeoProc(const GrShaderCaps* shaderCaps,
                    uint32_t gpTypeFlags,
                    const SkPMColor4f& color,
@@ -275,7 +279,7 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
     uint32_t flags = 0;
     if (d->fRandom->nextBool()) {
         flags |= kColorAttribute_GPFlag;
@@ -296,7 +300,7 @@
         flags |= kLocalCoordAttribute_GPFlag;
     }
 
-    return DefaultGeoProc::Make(d->caps()->shaderCaps(),
+    return DefaultGeoProc::Make(d->allocator(), d->caps()->shaderCaps(),
                                 flags,
                                 SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                 GrTest::TestColorXform(d->fRandom),
@@ -307,11 +311,12 @@
 }
 #endif
 
-sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::Make(const GrShaderCaps* shaderCaps,
-                                                         const Color& color,
-                                                         const Coverage& coverage,
-                                                         const LocalCoords& localCoords,
-                                                         const SkMatrix& viewMatrix) {
+GrGeometryProcessor* GrDefaultGeoProcFactory::Make(SkArenaAlloc* arena,
+                                                   const GrShaderCaps* shaderCaps,
+                                                   const Color& color,
+                                                   const Coverage& coverage,
+                                                   const LocalCoords& localCoords,
+                                                   const SkMatrix& viewMatrix) {
     uint32_t flags = 0;
     if (Color::kPremulGrColorAttribute_Type == color.fType) {
         flags |= kColorAttribute_GPFlag;
@@ -330,7 +335,8 @@
     uint8_t inCoverage = coverage.fCoverage;
     bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
 
-    return DefaultGeoProc::Make(shaderCaps,
+    return DefaultGeoProc::Make(arena,
+                                shaderCaps,
                                 flags,
                                 color.fColor,
                                 color.fColorSpaceXform,
@@ -340,12 +346,12 @@
                                 inCoverage);
 }
 
-sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
-                                                                     const GrShaderCaps* shaderCaps,
-                                                                     const Color& color,
-                                                                     const Coverage& coverage,
-                                                                     const LocalCoords& localCoords,
-                                                                     const SkMatrix& viewMatrix) {
+GrGeometryProcessor* GrDefaultGeoProcFactory::MakeForDeviceSpace(SkArenaAlloc* arena,
+                                                                 const GrShaderCaps* shaderCaps,
+                                                                 const Color& color,
+                                                                 const Coverage& coverage,
+                                                                 const LocalCoords& localCoords,
+                                                                 const SkMatrix& viewMatrix) {
     SkMatrix invert = SkMatrix::I();
     if (LocalCoords::kUnused_Type != localCoords.fType) {
         SkASSERT(LocalCoords::kUsePosition_Type == localCoords.fType);
@@ -359,5 +365,5 @@
     }
 
     LocalCoords inverted(LocalCoords::kUsePosition_Type, &invert);
-    return Make(shaderCaps, color, coverage, inverted, SkMatrix::I());
+    return Make(arena, shaderCaps, color, coverage, inverted, SkMatrix::I());
 }
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index fb61567..3f7fffa 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -76,22 +76,24 @@
         const SkMatrix* fMatrix;
     };
 
-    sk_sp<GrGeometryProcessor> Make(const GrShaderCaps*,
-                                    const Color&,
-                                    const Coverage&,
-                                    const LocalCoords&,
-                                    const SkMatrix& viewMatrix);
+    GrGeometryProcessor* Make(SkArenaAlloc*,
+                              const GrShaderCaps*,
+                              const Color&,
+                              const Coverage&,
+                              const LocalCoords&,
+                              const SkMatrix& viewMatrix);
 
     /*
      * Use this factory to create a GrGeometryProcessor that expects a device space vertex position
      * attribute. The view matrix must still be provided to compute correctly transformed
      * coordinates for GrFragmentProcessors. It may fail if the view matrix is not invertible.
      */
-    sk_sp<GrGeometryProcessor> MakeForDeviceSpace(const GrShaderCaps*,
-                                                  const Color&,
-                                                  const Coverage&,
-                                                  const LocalCoords&,
-                                                  const SkMatrix& viewMatrix);
+    GrGeometryProcessor* MakeForDeviceSpace(SkArenaAlloc*,
+                                            const GrShaderCaps*,
+                                            const Color&,
+                                            const Coverage&,
+                                            const LocalCoords&,
+                                            const SkMatrix& viewMatrix);
 };
 
 #endif
diff --git a/src/gpu/GrDistanceFieldGenFromVector.cpp b/src/gpu/GrDistanceFieldGenFromVector.cpp
index 29aa16d..11e375c 100644
--- a/src/gpu/GrDistanceFieldGenFromVector.cpp
+++ b/src/gpu/GrDistanceFieldGenFromVector.cpp
@@ -763,7 +763,7 @@
         workingPath = path;
     }
 
-    if (!IsDistanceFieldSupportedFillType(workingPath.getFillType())) {
+    if (!IsDistanceFieldSupportedFillType(workingPath.getNewFillType())) {
         return false;
     }
 
@@ -829,15 +829,19 @@
                 kOutside = 1
             } dfSign;
 
-            if (workingPath.getFillType() == SkPath::kWinding_FillType) {
-                dfSign = windingNumber ? kInside : kOutside;
-            } else if (workingPath.getFillType() == SkPath::kInverseWinding_FillType) {
-                dfSign = windingNumber ? kOutside : kInside;
-            } else if (workingPath.getFillType() == SkPath::kEvenOdd_FillType) {
-                dfSign = (windingNumber % 2) ? kInside : kOutside;
-            } else {
-                SkASSERT(workingPath.getFillType() == SkPath::kInverseEvenOdd_FillType);
-                dfSign = (windingNumber % 2) ? kOutside : kInside;
+            switch (workingPath.getNewFillType()) {
+                case SkPathFillType::kWinding:
+                    dfSign = windingNumber ? kInside : kOutside;
+                    break;
+                case SkPathFillType::kInverseWinding:
+                    dfSign = windingNumber ? kOutside : kInside;
+                    break;
+                case SkPathFillType::kEvenOdd:
+                    dfSign = (windingNumber % 2) ? kInside : kOutside;
+                    break;
+                case SkPathFillType::kInverseEvenOdd:
+                    dfSign = (windingNumber % 2) ? kOutside : kInside;
+                    break;
             }
 
             // The winding number at the end of a scanline should be zero.
diff --git a/src/gpu/GrDistanceFieldGenFromVector.h b/src/gpu/GrDistanceFieldGenFromVector.h
index 8e8d1b3..0362166 100644
--- a/src/gpu/GrDistanceFieldGenFromVector.h
+++ b/src/gpu/GrDistanceFieldGenFromVector.h
@@ -30,10 +30,10 @@
                                      const SkPath& path, const SkMatrix& viewMatrix,
                                      int width, int height, size_t rowBytes);
 
-inline bool IsDistanceFieldSupportedFillType(SkPath::FillType fFillType)
+inline bool IsDistanceFieldSupportedFillType(SkPathFillType fFillType)
 {
-    return (SkPath::kEvenOdd_FillType == fFillType ||
-            SkPath::kInverseEvenOdd_FillType == fFillType);
+    return (SkPathFillType::kEvenOdd == fFillType ||
+            SkPathFillType::kInverseEvenOdd == fFillType);
 }
 
 #endif
diff --git a/src/gpu/GrDrawOpAtlas.cpp b/src/gpu/GrDrawOpAtlas.cpp
index b41d34b..e91f223b 100644
--- a/src/gpu/GrDrawOpAtlas.cpp
+++ b/src/gpu/GrDrawOpAtlas.cpp
@@ -9,6 +9,7 @@
 
 #include "include/gpu/GrContext.h"
 #include "include/gpu/GrTexture.h"
+#include "src/core/SkOpts.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrOnFlushResourceProvider.h"
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index ba89eca..94c7015 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -128,7 +128,7 @@
             GrOpsTask* curOpsTask = fRenderTasks[i]->asOpsTask();
 
             if (prevOpsTask && curOpsTask) {
-                SkASSERT(prevOpsTask->fTarget.get() != curOpsTask->fTarget.get());
+                SkASSERT(prevOpsTask->fTargetView != curOpsTask->fTargetView);
             }
 
             prevOpsTask = curOpsTask;
@@ -581,7 +581,7 @@
         renderTask->prePrepare(fContext);
     }
 
-    ddl->fOpPOD = fContext->priv().detachOpPOD();
+    ddl->fRecordTimeData = fContext->priv().detachRecordTimeAllocator();
 
     if (fPathRendererChain) {
         if (auto ccpr = fPathRendererChain->getCoverageCountingPathRenderer()) {
@@ -593,7 +593,7 @@
 }
 
 void GrDrawingManager::copyRenderTasksFromDDL(const SkDeferredDisplayList* ddl,
-                                          GrRenderTargetProxy* newDest) {
+                                              GrRenderTargetProxy* newDest) {
     SkDEBUGCODE(this->validate());
 
     if (fActiveOpsTask) {
@@ -670,15 +670,17 @@
     }
 }
 
-sk_sp<GrOpsTask> GrDrawingManager::newOpsTask(sk_sp<GrRenderTargetProxy> rtp, bool managedOpsTask) {
+sk_sp<GrOpsTask> GrDrawingManager::newOpsTask(GrSurfaceProxyView surfaceView,
+                                              bool managedOpsTask) {
     SkDEBUGCODE(this->validate());
     SkASSERT(fContext);
 
-    this->closeRenderTasksForNewRenderTask(rtp.get());
+    GrSurfaceProxy* proxy = surfaceView.proxy();
+    this->closeRenderTasksForNewRenderTask(proxy);
 
-    sk_sp<GrOpsTask> opsTask(new GrOpsTask(fContext->priv().refOpMemoryPool(), rtp,
-                                           fContext->priv().auditTrail()));
-    SkASSERT(rtp->getLastRenderTask() == opsTask.get());
+    sk_sp<GrOpsTask> opsTask(new GrOpsTask(fContext->priv().refOpMemoryPool(),
+                                           std::move(surfaceView), fContext->priv().auditTrail()));
+    SkASSERT(proxy->getLastRenderTask() == opsTask.get());
 
     if (managedOpsTask) {
         fDAG.add(opsTask);
@@ -707,14 +709,15 @@
 }
 
 void GrDrawingManager::newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy,
-                                         std::unique_ptr<sk_sp<GrSemaphore>[]> semaphores,
+                                         std::unique_ptr<std::unique_ptr<GrSemaphore>[]> semaphores,
                                          int numSemaphores) {
     SkDEBUGCODE(this->validate());
     SkASSERT(fContext);
 
     const GrCaps& caps = *fContext->priv().caps();
 
-    sk_sp<GrWaitRenderTask> waitTask = sk_make_sp<GrWaitRenderTask>(proxy, std::move(semaphores),
+    sk_sp<GrWaitRenderTask> waitTask = sk_make_sp<GrWaitRenderTask>(GrSurfaceProxyView(proxy),
+                                                                    std::move(semaphores),
                                                                     numSemaphores);
     if (fReduceOpsTaskSplitting) {
         GrRenderTask* lastTask = proxy->getLastRenderTask();
@@ -745,7 +748,7 @@
         }
         fDAG.add(waitTask);
     } else {
-        if (fActiveOpsTask && (fActiveOpsTask->fTarget == proxy)) {
+        if (fActiveOpsTask && (fActiveOpsTask->fTargetView.proxy() == proxy.get())) {
             SkASSERT(proxy->getLastRenderTask() == fActiveOpsTask);
             fDAG.addBeforeLast(waitTask);
             // In this case we keep the current renderTask open but just insert the new waitTask
@@ -808,26 +811,28 @@
     SkDEBUGCODE(this->validate());
 }
 
-bool GrDrawingManager::newCopyRenderTask(sk_sp<GrSurfaceProxy> srcProxy,
+bool GrDrawingManager::newCopyRenderTask(GrSurfaceProxyView srcView,
                                          const SkIRect& srcRect,
-                                         sk_sp<GrSurfaceProxy> dstProxy,
+                                         GrSurfaceProxyView dstView,
                                          const SkIPoint& dstPoint) {
     SkDEBUGCODE(this->validate());
     SkASSERT(fContext);
 
-    this->closeRenderTasksForNewRenderTask(dstProxy.get());
+    this->closeRenderTasksForNewRenderTask(dstView.proxy());
     const GrCaps& caps = *fContext->priv().caps();
 
+    GrSurfaceProxy* srcProxy = srcView.proxy();
+
     GrRenderTask* task =
-            fDAG.add(GrCopyRenderTask::Make(srcProxy, srcRect, dstProxy, dstPoint, &caps));
+            fDAG.add(GrCopyRenderTask::Make(std::move(srcView), srcRect, std::move(dstView),
+                                            dstPoint, &caps));
     if (!task) {
         return false;
     }
 
-
     // We always say GrMipMapped::kNo here since we are always just copying from the base layer to
     // another base layer. We don't need to make sure the whole mip map chain is valid.
-    task->addDependency(srcProxy.get(), GrMipMapped::kNo, GrTextureResolveManager(this), caps);
+    task->addDependency(srcProxy, GrMipMapped::kNo, GrTextureResolveManager(this), caps);
     task->makeClosed(caps);
 
     // We have closed the previous active oplist but since a new oplist isn't being added there
@@ -914,8 +919,10 @@
     sk_sp<GrRenderTargetProxy> renderTargetProxy(sk_ref_sp(sProxy->asRenderTargetProxy()));
 
     GrSurfaceOrigin origin = renderTargetProxy->origin();
-    GrSwizzle texSwizzle = renderTargetProxy->textureSwizzle();
-    GrSwizzle outSwizzle = renderTargetProxy->outputSwizzle();
+    GrSwizzle texSwizzle = fContext->priv().caps()->getTextureSwizzle(sProxy->backendFormat(),
+                                                                      colorType);
+    GrSwizzle outSwizzle = fContext->priv().caps()->getOutputSwizzle(sProxy->backendFormat(),
+                                                                     colorType);
 
     return std::unique_ptr<GrRenderTargetContext>(
             new GrRenderTargetContext(fContext,
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index 72f3211..4d46aaa 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -26,6 +26,7 @@
 class GrRenderTargetContext;
 class GrRenderTargetProxy;
 class GrSoftwarePathRenderer;
+class GrSurfaceProxyView;
 class GrTextureContext;
 class GrTextureResolveRenderTask;
 class SkDeferredDisplayList;
@@ -48,7 +49,7 @@
 
     // A managed opsTask is controlled by the drawing manager (i.e., sorted & flushed with the
     // others). An unmanaged one is created and used by the onFlushCallback.
-    sk_sp<GrOpsTask> newOpsTask(sk_sp<GrRenderTargetProxy>, bool managedOpsTask);
+    sk_sp<GrOpsTask> newOpsTask(GrSurfaceProxyView, bool managedOpsTask);
 
     // Create a render task that can resolve MSAA and/or regenerate mipmap levels on proxies. This
     // method will only add the new render task to the list. It is up to the caller to call
@@ -60,7 +61,8 @@
     // work (even to other proxies) that has already been recorded or will be recorded later. The
     // only guarantee is that future work to the passed in proxy will wait on the semaphores to be
     // signaled.
-    void newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy, std::unique_ptr<sk_sp<GrSemaphore>[]>,
+    void newWaitRenderTask(sk_sp<GrSurfaceProxy> proxy,
+                           std::unique_ptr<std::unique_ptr<GrSemaphore>[]>,
                            int numSemaphores);
 
     // Create a new render task which copies the pixels from the srcProxy into the dstBuffer. This
@@ -72,14 +74,14 @@
                                    GrColorType surfaceColorType, GrColorType dstColorType,
                                    sk_sp<GrGpuBuffer> dstBuffer, size_t dstOffset);
 
-    // Creates a new render task which copies a pixel rectangle from srcProxy into dstProxy. The src
+    // Creates a new render task which copies a pixel rectangle from srcView into dstView. The src
     // pixels copied are specified by srcRect. They are copied to a rect of the same size in
     // dstProxy with top left at dstPoint. If the src rect is clipped by the src bounds then  pixel
     // values in the dst rect corresponding to the area clipped by the src rect are not overwritten.
     // This method is not guaranteed to succeed depending on the type of surface, formats, etc, and
     // the backend-specific limitations.
-    bool newCopyRenderTask(sk_sp<GrSurfaceProxy> srcProxy, const SkIRect& srcRect,
-                           sk_sp<GrSurfaceProxy> dstProxy, const SkIPoint& dstPoint);
+    bool newCopyRenderTask(GrSurfaceProxyView srcView, const SkIRect& srcRect,
+                           GrSurfaceProxyView dstView, const SkIPoint& dstPoint);
 
     GrRecordingContext* getContext() { return fContext; }
 
diff --git a/src/gpu/GrFragmentProcessor.cpp b/src/gpu/GrFragmentProcessor.cpp
index 1e1f2e4..b836be4 100644
--- a/src/gpu/GrFragmentProcessor.cpp
+++ b/src/gpu/GrFragmentProcessor.cpp
@@ -48,10 +48,9 @@
 }
 
 void GrFragmentProcessor::visitProxies(const GrOp::VisitProxyFunc& func) {
-    GrFragmentProcessor::TextureAccessIter iter(this);
-    while (const TextureSampler* sampler = iter.next()) {
-        bool mipped = (GrSamplerState::Filter::kMipMap == sampler->samplerState().filter());
-        func(sampler->proxy(), GrMipMapped(mipped));
+    for (auto [sampler, fp] : FPTextureSamplerRange(*this)) {
+        bool mipped = (GrSamplerState::Filter::kMipMap == sampler.samplerState().filter());
+        func(sampler.proxy(), GrMipMapped(mipped));
     }
 }
 
@@ -70,10 +69,8 @@
 }
 
 void GrFragmentProcessor::addCoordTransform(GrCoordTransform* transform) {
-    transform->setComputeInVertexShader(this->computeLocalCoordsInVertexShader());
     fCoordTransforms.push_back(transform);
-    fFlags |= kUsesLocalCoords_Flag;
-    SkDEBUGCODE(transform->setInProcessor();)
+    fFlags |= kHasCoordTranforms_Flag;
 }
 
 #ifdef SK_DEBUG
@@ -95,8 +92,8 @@
 #endif
 
 int GrFragmentProcessor::registerChildProcessor(std::unique_ptr<GrFragmentProcessor> child) {
-    if (child->usesLocalCoords()) {
-        fFlags |= kUsesLocalCoords_Flag;
+    if (child->fFlags & kHasCoordTranforms_Flag) {
+        fFlags |= kHasCoordTranforms_Flag;
     }
     fRequestedFeatures |= child->fRequestedFeatures;
 
@@ -112,7 +109,7 @@
     }
     int count = this->numCoordTransforms();
     for (int i = 0; i < count; ++i) {
-        if (!this->coordTransform(i).hasSameEffectAs(that.coordTransform(i))) {
+        if (!this->coordTransform(i).hasSameEffectiveMatrix(that.coordTransform(i))) {
             return false;
         }
     }
@@ -172,7 +169,7 @@
                     GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
 
                     fragBuilder->codeAppendf("%s = %s.%s;",
-                                             args.fOutputColor, args.fInputColor, swizzle.c_str());
+                            args.fOutputColor, args.fInputColor, swizzle.asString().c_str());
                 }
             };
             return new GLFP;
@@ -389,13 +386,7 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrFragmentProcessor::Iter::Iter(const GrPipeline& pipeline) {
-    for (int i = pipeline.numFragmentProcessors() - 1; i >= 0; --i) {
-        fFPStack.push_back(&pipeline.getFragmentProcessor(i));
-    }
-}
-
-GrFragmentProcessor::Iter::Iter(const GrPaint& paint) {
+GrFragmentProcessor::CIter::CIter(const GrPaint& paint) {
     for (int i = paint.numCoverageFragmentProcessors() - 1; i >= 0; --i) {
         fFPStack.push_back(paint.getCoverageFragmentProcessor(i));
     }
@@ -404,28 +395,34 @@
     }
 }
 
-const GrFragmentProcessor* GrFragmentProcessor::Iter::next() {
-    if (fFPStack.empty()) {
-        return nullptr;
+GrFragmentProcessor::CIter::CIter(const GrProcessorSet& set) {
+    for (int i = set.numCoverageFragmentProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(set.coverageFragmentProcessor(i));
     }
-    const GrFragmentProcessor* back = fFPStack.back();
-    fFPStack.pop_back();
-    for (int i = back->numChildProcessors() - 1; i >= 0; --i) {
-        fFPStack.push_back(&back->childProcessor(i));
+    for (int i = set.numColorFragmentProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(set.colorFragmentProcessor(i));
     }
-    return back;
+}
+
+GrFragmentProcessor::CIter::CIter(const GrPipeline& pipeline) {
+    for (int i = pipeline.numFragmentProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(&pipeline.getFragmentProcessor(i));
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-GrFragmentProcessor::TextureSampler::TextureSampler(sk_sp<GrTextureProxy> proxy,
+GrFragmentProcessor::TextureSampler::TextureSampler(sk_sp<GrSurfaceProxy> proxy,
                                                     const GrSamplerState& samplerState) {
     this->reset(std::move(proxy), samplerState);
 }
 
-void GrFragmentProcessor::TextureSampler::reset(sk_sp<GrTextureProxy> proxy,
+void GrFragmentProcessor::TextureSampler::reset(sk_sp<GrSurfaceProxy> proxy,
                                                 const GrSamplerState& samplerState) {
+    SkASSERT(proxy->asTextureProxy());
     fProxy = std::move(proxy);
     fSamplerState = samplerState;
-    fSamplerState.setFilterMode(SkTMin(samplerState.filter(), this->proxy()->highestFilterMode()));
+    fSamplerState.setFilterMode(
+            SkTMin(samplerState.filter(),
+                   GrTextureProxy::HighestFilterMode(fProxy->backendFormat().textureType())));
 }
diff --git a/src/gpu/GrFragmentProcessor.h b/src/gpu/GrFragmentProcessor.h
index 21e27b8..03d22ae 100644
--- a/src/gpu/GrFragmentProcessor.h
+++ b/src/gpu/GrFragmentProcessor.h
@@ -111,8 +111,9 @@
     int numCoordTransforms() const { return fCoordTransforms.count(); }
 
     /** Returns the coordinate transformation at index. index must be valid according to
-        numTransforms(). */
+        numCoordTransforms(). */
     const GrCoordTransform& coordTransform(int index) const { return *fCoordTransforms[index]; }
+    GrCoordTransform& coordTransform(int index) { return *fCoordTransforms[index]; }
 
     const SkTArray<GrCoordTransform*, true>& coordTransforms() const {
         return fCoordTransforms;
@@ -120,28 +121,31 @@
 
     int numChildProcessors() const { return fChildProcessors.count(); }
 
+    GrFragmentProcessor& childProcessor(int index) { return *fChildProcessors[index]; }
     const GrFragmentProcessor& childProcessor(int index) const { return *fChildProcessors[index]; }
 
     SkDEBUGCODE(bool isInstantiated() const;)
 
-    /** Do any of the coordtransforms for this processor require local coords? */
-    bool usesLocalCoords() const { return SkToBool(fFlags & kUsesLocalCoords_Flag); }
-
-    bool computeLocalCoordsInVertexShader() const {
-        return SkToBool(fFlags & kComputeLocalCoordsInVertexShader_Flag);
+    /** Do any of the coord transforms for this processor require local coords? */
+    bool usesLocalCoords() const {
+        // If the processor is sampled with explicit coords then we do not need to apply the
+        // coord transforms in the vertex shader to the local coords.
+        return SkToBool(fFlags & kHasCoordTranforms_Flag) &&
+               SkToBool(fFlags & kCoordTransformsApplyToLocalCoords_Flag);
     }
 
-    void setComputeLocalCoordsInVertexShader(bool value) const {
+    bool coordTransformsApplyToLocalCoords() const {
+        return SkToBool(fFlags & kCoordTransformsApplyToLocalCoords_Flag);
+    }
+
+    void setSampledWithExplicitCoords(bool value) {
         if (value) {
-            fFlags |= kComputeLocalCoordsInVertexShader_Flag;
+            fFlags &= ~kCoordTransformsApplyToLocalCoords_Flag;
         } else {
-            fFlags &= ~kComputeLocalCoordsInVertexShader_Flag;
+            fFlags |= kCoordTransformsApplyToLocalCoords_Flag;
         }
-        for (GrCoordTransform* transform : fCoordTransforms) {
-            transform->setComputeInVertexShader(value);
-        }
-        for (const auto& child : fChildProcessors) {
-            child->setComputeLocalCoordsInVertexShader(value);
+        for (auto& child : fChildProcessors) {
+            child->setSampledWithExplicitCoords(value);
         }
     }
 
@@ -193,72 +197,114 @@
      */
     bool isEqual(const GrFragmentProcessor& that) const;
 
-    /**
-     * Pre-order traversal of a FP hierarchy, or of the forest of FPs in a GrPipeline. In the latter
-     * case the tree rooted at each FP in the GrPipeline is visited successively.
-     */
-    class Iter : public SkNoncopyable {
-    public:
-        explicit Iter(const GrFragmentProcessor* fp) { fFPStack.push_back(fp); }
-        explicit Iter(const GrPipeline& pipeline);
-        explicit Iter(const GrPaint&);
-        const GrFragmentProcessor* next();
+    void visitProxies(const GrOp::VisitProxyFunc& func);
 
-    private:
-        SkSTArray<4, const GrFragmentProcessor*, true> fFPStack;
+    // A pre-order traversal iterator over a hierarchy of FPs. It can also iterate over all the FP
+    // hierarchies rooted in a GrPaint, GrProcessorSet, or GrPipeline. For these collections it
+    // iterates the tree rooted at each color FP and then each coverage FP.
+    //
+    // Iter is the non-const version and CIter is the const version.
+    //
+    // An iterator is constructed from one of the srcs and used like this:
+    //   for (GrFragmentProcessor::Iter iter(pipeline); iter; ++iter) {
+    //       GrFragmentProcessor& fp = *iter;
+    //   }
+    // The exit test for the loop is using Iter's operator bool().
+    // To use a range-for loop instead see CIterRange below.
+    class Iter;
+    class CIter;
+
+    // Used to implement a range-for loop using CIter. Src is one of GrFragmentProcessor,
+    // GrPaint, GrProcessorSet, or GrPipeline. Type aliases for these defined below.
+    // Example usage:
+    //   for (const auto& fp : GrFragmentProcessor::PaintRange(paint)) {
+    //       if (fp.usesLocalCoords()) {
+    //       ...
+    //       }
+    //   }
+    template <typename Src> class CIterRange;
+    // Like CIterRange but non const and only constructable from GrFragmentProcessor. This could
+    // support GrPaint as it owns non-const FPs but no need for it as of now.
+    //   for (auto& fp0 : GrFragmentProcessor::IterRange(fp)) {
+    //       ...
+    //   }
+    class IterRange;
+
+    // We would use template deduction guides for Iter/CIter but for:
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79501
+    // Instead we use these specialized type aliases to make it prettier
+    // to construct Iters for particular sources of FPs.
+    using FPCRange = CIterRange<GrFragmentProcessor>;
+    using PaintCRange = CIterRange<GrPaint>;
+
+    // Implementation details for iterators that walk an array of Items owned by a set of FPs.
+    using CountFn = int (GrFragmentProcessor::*)() const;
+    // Defined GetFn to be a member function that returns an Item by index. The function itself is
+    // const if Item is a const type and non-const if Item is non-const.
+    template <typename Item, bool IsConst = std::is_const<Item>::value> struct GetT;
+    template <typename Item> struct GetT<Item, false> {
+        using GetFn = Item& (GrFragmentProcessor::*)(int);
     };
-
-    /**
-     * Iterates over all the Ts owned by a GrFragmentProcessor and its children or over all the Ts
-     * owned by the forest of GrFragmentProcessors in a GrPipeline. FPs are visited in the same
-     * order as Iter and each of an FP's Ts are visited in order.
-     */
-    template <typename T, int (GrFragmentProcessor::*COUNT)() const,
-              const T& (GrFragmentProcessor::*GET)(int)const>
-    class FPItemIter : public SkNoncopyable {
-    public:
-        explicit FPItemIter(const GrFragmentProcessor* fp)
-                : fCurrFP(nullptr)
-                , fCTIdx(0)
-                , fFPIter(fp) {
-            fCurrFP = fFPIter.next();
-        }
-        explicit FPItemIter(const GrPipeline& pipeline)
-                : fCurrFP(nullptr)
-                , fCTIdx(0)
-                , fFPIter(pipeline) {
-            fCurrFP = fFPIter.next();
-        }
-
-        const T* next() {
-            if (!fCurrFP) {
-                return nullptr;
-            }
-            while (fCTIdx == (fCurrFP->*COUNT)()) {
-                fCTIdx = 0;
-                fCurrFP = fFPIter.next();
-                if (!fCurrFP) {
-                    return nullptr;
-                }
-            }
-            return &(fCurrFP->*GET)(fCTIdx++);
-        }
-
-    private:
-        const GrFragmentProcessor*  fCurrFP;
-        int                         fCTIdx;
-        GrFragmentProcessor::Iter   fFPIter;
+    template <typename Item> struct GetT<Item, true> {
+        using GetFn = const Item& (GrFragmentProcessor::*)(int) const;
     };
+    template <typename Item> using GetFn = typename GetT<Item>::GetFn;
+    // This is an iterator over the Items owned by a (collection of) FP. CountFn is a FP member that
+    // gets the number of Items owned by each FP and GetFn is a member that gets them by index.
+    template <typename Item, CountFn Count, GetFn<Item> Get> class FPItemIter;
 
-    using CoordTransformIter = FPItemIter<GrCoordTransform,
+    // Loops over all the GrCoordTransforms owned by GrFragmentProcessors. The possible sources for
+    // the iteration are the same as those for Iter and the FPs are walked in the same order as
+    // Iter. This provides access to the coord transform and the FP that owns it. Example usage:
+    //   for (GrFragmentProcessor::CoordTransformIter iter(pipeline); iter; ++iter) {
+    //       // transform is const GrCoordTransform& and owningFP is const GrFragmentProcessor&.
+    //       auto [transform, owningFP] = *iter;
+    //       ...
+    //   }
+    // See the ranges below to make this simpler a la range-for loops.
+    using CoordTransformIter = FPItemIter<const GrCoordTransform,
                                           &GrFragmentProcessor::numCoordTransforms,
                                           &GrFragmentProcessor::coordTransform>;
+    // Same as CoordTransformIter but for TextureSamplers:
+    //   for (GrFragmentProcessor::TextureSamplerIter iter(pipeline); iter; ++iter) {
+    //       // TextureSamplerIter is const GrFragmentProcessor::TextureSampler& and
+    //       // owningFP is const GrFragmentProcessor&.
+    //       auto [sampler, owningFP] = *iter;
+    //       ...
+    //   }
+    // See the ranges below to make this simpler a la range-for loops.
+    using TextureSamplerIter = FPItemIter<const TextureSampler,
+                                          &GrFragmentProcessor::numTextureSamplers,
+                                          &GrFragmentProcessor::textureSampler>;
 
-    using TextureAccessIter = FPItemIter<TextureSampler,
-                                         &GrFragmentProcessor::numTextureSamplers,
-                                         &GrFragmentProcessor::textureSampler>;
+    // Implementation detail for using CoordTransformIter and TextureSamplerIter in range-for loops.
+    template <typename Src, typename ItemIter> class FPItemRange;
 
-    void visitProxies(const GrOp::VisitProxyFunc& func);
+    // These allow iteration over coord transforms/texture samplers for various FP sources via
+    // range-for loops. An example usage for looping over the coord transforms in a pipeline:
+    // for (auto [transform, fp] : GrFragmentProcessor::PipelineCoordTransformRange(pipeline)) {
+    //     ...
+    // }
+    // Only the combinations of FP sources and iterable things have been defined but it is easy
+    // to add more as they become useful. Maybe someday we'll have template argument deduction
+    // with guides for type aliases and the sources can be removed from the type aliases:
+    // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1021r5.html
+    using PipelineCoordTransformRange = FPItemRange<const GrPipeline, CoordTransformIter>;
+    using PipelineTextureSamplerRange = FPItemRange<const GrPipeline, TextureSamplerIter>;
+    using FPTextureSamplerRange = FPItemRange<const GrFragmentProcessor, TextureSamplerIter>;
+    using ProcessorSetTextureSamplerRange = FPItemRange<const GrProcessorSet, TextureSamplerIter>;
+
+    // Not used directly.
+    using NonConstCoordTransformIter =
+            FPItemIter<GrCoordTransform, &GrFragmentProcessor::numCoordTransforms,
+                       &GrFragmentProcessor::coordTransform>;
+    // Iterator over non-const GrCoordTransforms owned by FP and its descendants.
+    using FPCoordTransformRange = FPItemRange<GrFragmentProcessor, NonConstCoordTransformIter>;
+
+    // Sentinel type for range-for using Iter.
+    class EndIter {};
+    // Sentinel type for range-for using FPItemIter.
+    class FPItemEndIter {};
 
 protected:
     enum OptimizationFlags : uint32_t {
@@ -282,17 +328,17 @@
      * callers must determine on their own if the sampling uses a decal strategy in any way, in
      * which case the texture may become transparent regardless of the color type.
      */
-    static OptimizationFlags ModulateForSamplerOptFlags(GrColorType colorType, bool samplingDecal) {
+    static OptimizationFlags ModulateForSamplerOptFlags(SkAlphaType alphaType, bool samplingDecal) {
         if (samplingDecal) {
             return kCompatibleWithCoverageAsAlpha_OptimizationFlag;
         } else {
-            return ModulateForClampedSamplerOptFlags(colorType);
+            return ModulateForClampedSamplerOptFlags(alphaType);
         }
     }
 
     // As above, but callers should somehow ensure or assert their sampler still uses clamping
-    static OptimizationFlags ModulateForClampedSamplerOptFlags(GrColorType colorType) {
-        if (!GrColorTypeHasAlpha(colorType)) {
+    static OptimizationFlags ModulateForClampedSamplerOptFlags(SkAlphaType alphaType) {
+        if (alphaType == kOpaque_SkAlphaType) {
             return kCompatibleWithCoverageAsAlpha_OptimizationFlag |
                    kPreservesOpaqueInput_OptimizationFlag;
         } else {
@@ -302,7 +348,7 @@
 
     GrFragmentProcessor(ClassID classID, OptimizationFlags optimizationFlags)
             : INHERITED(classID)
-            , fFlags(optimizationFlags | kComputeLocalCoordsInVertexShader_Flag) {
+            , fFlags(optimizationFlags | kCoordTransformsApplyToLocalCoords_Flag) {
         SkASSERT((optimizationFlags & ~kAll_OptimizationFlags) == 0);
     }
 
@@ -373,6 +419,9 @@
     inline static const TextureSampler& IthTextureSampler(int i);
 
 private:
+    // Implementation details of Iter and CIter.
+    template <typename> class IterBase;
+
     virtual SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& /* inputColor */) const {
         SK_ABORT("Subclass must override this if advertising this optimization.");
     }
@@ -399,11 +448,11 @@
 
     enum PrivateFlags {
         kFirstPrivateFlag = kAll_OptimizationFlags + 1,
-        kUsesLocalCoords_Flag = kFirstPrivateFlag,
-        kComputeLocalCoordsInVertexShader_Flag = kFirstPrivateFlag << 1,
+        kHasCoordTranforms_Flag = kFirstPrivateFlag,
+        kCoordTransformsApplyToLocalCoords_Flag = kFirstPrivateFlag << 1,
     };
 
-    mutable uint32_t fFlags = kComputeLocalCoordsInVertexShader_Flag;
+    uint32_t fFlags = kCoordTransformsApplyToLocalCoords_Flag;
 
     int fTextureSamplerCnt = 0;
 
@@ -430,11 +479,11 @@
             : fProxy(that.fProxy)
             , fSamplerState(that.fSamplerState) {}
 
-    TextureSampler(sk_sp<GrTextureProxy>, const GrSamplerState& = GrSamplerState::ClampNearest());
+    TextureSampler(sk_sp<GrSurfaceProxy>, const GrSamplerState& = GrSamplerState::ClampNearest());
 
     TextureSampler& operator=(const TextureSampler&) = delete;
 
-    void reset(sk_sp<GrTextureProxy>, const GrSamplerState&);
+    void reset(sk_sp<GrSurfaceProxy>, const GrSamplerState&);
 
     bool operator==(const TextureSampler& that) const {
         return this->proxy()->underlyingUniqueID() == that.proxy()->underlyingUniqueID() &&
@@ -451,14 +500,14 @@
         return fProxy->peekTexture();
     }
 
-    GrTextureProxy* proxy() const { return fProxy.get(); }
+    GrSurfaceProxy* proxy() const { return fProxy.get(); }
     const GrSamplerState& samplerState() const { return fSamplerState; }
     const GrSwizzle& swizzle() const { return this->proxy()->textureSwizzle(); }
 
     bool isInitialized() const { return SkToBool(fProxy.get()); }
 
 private:
-    sk_sp<GrTextureProxy> fProxy;
+    sk_sp<GrSurfaceProxy> fProxy;
     GrSamplerState        fSamplerState;
 };
 
@@ -472,4 +521,126 @@
 
 GR_MAKE_BITFIELD_OPS(GrFragmentProcessor::OptimizationFlags)
 
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename FP> class GrFragmentProcessor::IterBase {
+public:
+    FP& operator*() const { return *fFPStack.back(); }
+    FP* operator->() const { return fFPStack.back(); }
+    operator bool() const { return !fFPStack.empty(); }
+    bool operator!=(const EndIter&) { return (bool)*this; }
+
+    // Because each iterator carries a stack we want to avoid copies.
+    IterBase(const IterBase&) = delete;
+    IterBase& operator=(const IterBase&) = delete;
+
+protected:
+    void increment();
+
+    IterBase() = default;
+    explicit IterBase(FP& fp) { fFPStack.push_back(&fp); }
+
+    SkSTArray<4, FP*, true> fFPStack;
+};
+
+template <typename FP> void GrFragmentProcessor::IterBase<FP>::increment() {
+    SkASSERT(!fFPStack.empty());
+    FP* back = fFPStack.back();
+    fFPStack.pop_back();
+    for (int i = back->numChildProcessors() - 1; i >= 0; --i) {
+        fFPStack.push_back(&back->childProcessor(i));
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GrFragmentProcessor::Iter : public IterBase<GrFragmentProcessor> {
+public:
+    explicit Iter(GrFragmentProcessor& fp) : IterBase(fp) {}
+    Iter& operator++() {
+        this->increment();
+        return *this;
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class GrFragmentProcessor::CIter : public IterBase<const GrFragmentProcessor> {
+public:
+    explicit CIter(const GrFragmentProcessor& fp) : IterBase(fp) {}
+    explicit CIter(const GrPaint&);
+    explicit CIter(const GrProcessorSet&);
+    explicit CIter(const GrPipeline&);
+    CIter& operator++() {
+        this->increment();
+        return *this;
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename Src> class GrFragmentProcessor::CIterRange {
+public:
+    explicit CIterRange(const Src& t) : fT(t) {}
+    CIter begin() const { return CIter(fT); }
+    EndIter end() const { return EndIter(); }
+
+private:
+    const Src& fT;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename Item, GrFragmentProcessor::CountFn Count, GrFragmentProcessor::GetFn<Item> Get>
+class GrFragmentProcessor::FPItemIter {
+public:
+    template <typename Src> explicit FPItemIter(Src& s);
+
+    std::pair<Item&, const GrFragmentProcessor&> operator*() const {
+        return {(*fFPIter.*Get)(fIndex), *fFPIter};
+    }
+    FPItemIter& operator++();
+    operator bool() const { return fFPIter; }
+    bool operator!=(const FPItemEndIter&) { return (bool)*this; }
+
+    FPItemIter(const FPItemIter&) = delete;
+    FPItemIter& operator=(const FPItemIter&) = delete;
+
+private:
+    typename std::conditional<std::is_const<Item>::value, CIter, Iter>::type fFPIter;
+    int fIndex;
+};
+
+template <typename Item, GrFragmentProcessor::CountFn Count, GrFragmentProcessor::GetFn<Item> Get>
+template <typename Src>
+GrFragmentProcessor::FPItemIter<Item, Count, Get>::FPItemIter(Src& s) : fFPIter(s), fIndex(-1) {
+    if (fFPIter) {
+        ++*this;
+    }
+}
+
+template <typename Item, GrFragmentProcessor::CountFn Count, GrFragmentProcessor::GetFn<Item> Get>
+GrFragmentProcessor::FPItemIter<Item, Count, Get>&
+GrFragmentProcessor::FPItemIter<Item, Count, Get>::operator++() {
+    ++fIndex;
+    if (fIndex < ((*fFPIter).*Count)()) {
+        return *this;
+    }
+    fIndex = 0;
+    do {} while (++fFPIter && !((*fFPIter).*Count)());
+    return *this;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename Src, typename ItemIter> class GrFragmentProcessor::FPItemRange {
+public:
+    FPItemRange(Src& src) : fSrc(src) {}
+    ItemIter begin() const { return ItemIter(fSrc); }
+    FPItemEndIter end() const { return FPItemEndIter(); }
+
+private:
+    Src& fSrc;
+};
+
 #endif
diff --git a/src/gpu/GrGeometryProcessor.h b/src/gpu/GrGeometryProcessor.h
index 6f596a5..966437a 100644
--- a/src/gpu/GrGeometryProcessor.h
+++ b/src/gpu/GrGeometryProcessor.h
@@ -12,10 +12,17 @@
 
 /**
  * A GrGeometryProcessor is a flexible method for rendering a primitive.  The GrGeometryProcessor
- * has complete control over vertex attributes and uniforms(aside from the render target) but it
+ * has complete control over vertex attributes and uniforms (aside from the render target) but it
  * must obey the same contract as any GrPrimitiveProcessor, specifically it must emit a color and
  * coverage into the fragment shader.  Where this color and coverage come from is completely the
  * responsibility of the GrGeometryProcessor.
+ *
+ * Note that all derived classes should hide their constructors and provide a Make factory
+ * function that takes an arena (except for CCPR-specific classes). This is because
+ * GrGeometryProcessor's are not ref-counted to must have some other mechanism for managing
+ * their lifetime. In particular, geometry processors can be created in either the
+ * record-time or flush-time arenas which defined their lifetimes (i.e., a DDLs life time in
+ * the first case and a single flush in the second case).
  */
 class GrGeometryProcessor : public GrPrimitiveProcessor {
 public:
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 746490a..43411bf 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -641,25 +641,59 @@
     this->stats()->incNumFinishFlushes();
     GrResourceProvider* resourceProvider = fContext->priv().resourceProvider();
 
-    if (this->caps()->semaphoreSupport()) {
-        for (int i = 0; i < info.fNumSemaphores; ++i) {
-            sk_sp<GrSemaphore> semaphore;
+    struct SemaphoreInfo {
+        std::unique_ptr<GrSemaphore> fSemaphore;
+        bool fDidCreate = false;
+    };
+
+    bool failedSemaphoreCreation = false;
+    std::unique_ptr<SemaphoreInfo[]> semaphoreInfos(new SemaphoreInfo[info.fNumSemaphores]);
+    if (this->caps()->semaphoreSupport() && info.fNumSemaphores) {
+        for (int i = 0; i < info.fNumSemaphores && !failedSemaphoreCreation; ++i) {
             if (info.fSignalSemaphores[i].isInitialized()) {
-                semaphore = resourceProvider->wrapBackendSemaphore(
+                semaphoreInfos[i].fSemaphore = resourceProvider->wrapBackendSemaphore(
                         info.fSignalSemaphores[i],
                         GrResourceProvider::SemaphoreWrapType::kWillSignal,
                         kBorrow_GrWrapOwnership);
             } else {
-                semaphore = resourceProvider->makeSemaphore(false);
+                semaphoreInfos[i].fSemaphore = resourceProvider->makeSemaphore(false);
+                semaphoreInfos[i].fDidCreate = true;
             }
-            this->insertSemaphore(semaphore);
-
-            if (!info.fSignalSemaphores[i].isInitialized()) {
-                info.fSignalSemaphores[i] = semaphore->backendSemaphore();
+            if (!semaphoreInfos[i].fSemaphore) {
+                semaphoreInfos[i].fDidCreate = false;
+                failedSemaphoreCreation = true;
+            }
+        }
+        if (!failedSemaphoreCreation) {
+            for (int i = 0; i < info.fNumSemaphores && !failedSemaphoreCreation; ++i) {
+                this->insertSemaphore(semaphoreInfos[i].fSemaphore.get());
             }
         }
     }
-    this->onFinishFlush(proxies, n, access, info, externalRequests);
+
+    // We always want to try flushing, so do that before checking if we failed semaphore creation.
+    if (!this->onFinishFlush(proxies, n, access, info, externalRequests) ||
+        failedSemaphoreCreation) {
+        // If we didn't do the flush or failed semaphore creations then none of the semaphores were
+        // submitted. Therefore the client can't wait on any of the semaphores. Additionally any
+        // semaphores we created here the client is not responsible for deleting so we must make
+        // sure they get deleted. We do this by changing the ownership from borrowed to owned.
+        for (int i = 0; i < info.fNumSemaphores; ++i) {
+            if (semaphoreInfos[i].fDidCreate) {
+                SkASSERT(semaphoreInfos[i].fSemaphore);
+                semaphoreInfos[i].fSemaphore->setIsOwned();
+            }
+        }
+        return GrSemaphoresSubmitted::kNo;
+    }
+
+    for (int i = 0; i < info.fNumSemaphores; ++i) {
+        if (!info.fSignalSemaphores[i].isInitialized()) {
+            SkASSERT(semaphoreInfos[i].fSemaphore);
+            info.fSignalSemaphores[i] = semaphoreInfos[i].fSemaphore->backendSemaphore();
+        }
+    }
+
     return this->caps()->semaphoreSupport() ? GrSemaphoresSubmitted::kYes
                                             : GrSemaphoresSubmitted::kNo;
 }
@@ -701,49 +735,40 @@
 #endif // GR_GPU_STATS
 #endif // GR_TEST_UTILS
 
+bool GrGpu::MipMapsAreCorrect(SkISize dimensions, const BackendTextureData* data, int numLevels) {
+    if (numLevels != 1 &&
+        numLevels != SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1) {
+        return false;
+    }
 
-bool GrGpu::MipMapsAreCorrect(int baseWidth, int baseHeight, GrMipMapped mipMapped,
-                              const SkPixmap srcData[], int numMipLevels) {
-    if (!srcData) {
+    if (!data || data->type() != BackendTextureData::Type::kPixmaps) {
         return true;
     }
 
-    if (baseWidth != srcData[0].width() || baseHeight != srcData[0].height()) {
+    if (data->pixmap(0).dimensions() != dimensions) {
         return false;
     }
 
-    if (mipMapped == GrMipMapped::kYes) {
-        if (numMipLevels != SkMipMap::ComputeLevelCount(baseWidth, baseHeight) + 1) {
+    SkColorType colorType = data->pixmap(0).colorType();
+    for (int i = 1; i < numLevels; ++i) {
+        dimensions = {SkTMax(1, dimensions.width() /2),
+                      SkTMax(1, dimensions.height()/2)};
+        if (dimensions != data->pixmap(i).dimensions()) {
             return false;
         }
-
-        SkColorType colorType = srcData[0].colorType();
-
-        int currentWidth = baseWidth;
-        int currentHeight = baseHeight;
-        for (int i = 1; i < numMipLevels; ++i) {
-            currentWidth = SkTMax(1, currentWidth / 2);
-            currentHeight = SkTMax(1, currentHeight / 2);
-
-            if (srcData[i].colorType() != colorType) { // all levels must have same colorType
-                return false;
-            }
-
-            if (srcData[i].width() != currentWidth || srcData[i].height() != currentHeight) {
-                return false;
-            }
+        if (colorType != data->pixmap(i).colorType()) {
+            return false;
         }
-    } else if (numMipLevels != 1) {
-        return false;
     }
-
     return true;
 }
 
-GrBackendTexture GrGpu::createBackendTexture(int w, int h, const GrBackendFormat& format,
-                                             GrMipMapped mipMapped, GrRenderable renderable,
-                                             const SkPixmap srcData[], int numMipLevels,
-                                             const SkColor4f* color, GrProtected isProtected) {
+GrBackendTexture GrGpu::createBackendTexture(SkISize dimensions,
+                                             const GrBackendFormat& format,
+                                             GrRenderable renderable,
+                                             const BackendTextureData* data,
+                                             int numMipLevels,
+                                             GrProtected isProtected) {
     const GrCaps* caps = this->caps();
 
     if (!format.isValid()) {
@@ -755,19 +780,26 @@
         return {};
     }
 
-    if (w < 1 || w > caps->maxTextureSize() || h < 1 || h > caps->maxTextureSize()) {
+    if (data && data->type() == BackendTextureData::Type::kPixmaps) {
+        auto ct = SkColorTypeToGrColorType(data->pixmap(0).colorType());
+        if (!caps->areColorTypeAndFormatCompatible(ct, format)) {
+            return {};
+        }
+    }
+
+    if (dimensions.isEmpty() || dimensions.width()  > caps->maxTextureSize() ||
+                                dimensions.height() > caps->maxTextureSize()) {
         return {};
     }
 
-    // TODO: maybe just ignore the mipMapped parameter in this case
-    if (mipMapped == GrMipMapped::kYes && !this->caps()->mipMapSupport()) {
+    if (numMipLevels > 1 && !this->caps()->mipMapSupport()) {
         return {};
     }
 
-    if (!MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels)) {
+    if (!MipMapsAreCorrect(dimensions, data, numMipLevels)) {
         return {};
     }
 
-    return this->onCreateBackendTexture(w, h, format, mipMapped, renderable,
-                                        srcData, numMipLevels, color, isProtected);
+    return this->onCreateBackendTexture(dimensions, format, renderable, data, numMipLevels,
+                                        isProtected);
 }
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 2ddf342..5fd9ea0 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -15,7 +15,6 @@
 #include "src/gpu/GrAllocator.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrOpsRenderPass.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrSamplePatternDictionary.h"
 #include "src/gpu/GrSwizzle.h"
 #include "src/gpu/GrTextureProducer.h"
@@ -342,7 +341,7 @@
             GrRenderTarget* renderTarget, GrSurfaceOrigin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) = 0;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) = 0;
 
     // Called by GrDrawingManager when flushing.
     // Provides a hook for post-flush actions (e.g. Vulkan command buffer submits). This will also
@@ -358,12 +357,12 @@
     virtual bool waitFence(GrFence, uint64_t timeout = 1000) = 0;
     virtual void deleteFence(GrFence) const = 0;
 
-    virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true) = 0;
-    virtual sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                    GrResourceProvider::SemaphoreWrapType wrapType,
-                                                    GrWrapOwnership ownership) = 0;
-    virtual void insertSemaphore(sk_sp<GrSemaphore> semaphore) = 0;
-    virtual void waitSemaphore(sk_sp<GrSemaphore> semaphore) = 0;
+    virtual std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(
+            bool isOwned = true) = 0;
+    virtual std::unique_ptr<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType, GrWrapOwnership ownership) = 0;
+    virtual void insertSemaphore(GrSemaphore* semaphore) = 0;
+    virtual void waitSemaphore(GrSemaphore* semaphore) = 0;
 
     virtual void checkFinishProcs() = 0;
 
@@ -372,7 +371,7 @@
      *  the backend, this may return a GrSemaphore. If so, other contexts should wait on that
      *  semaphore before using this texture.
      */
-    virtual sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) = 0;
+    virtual std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) = 0;
 
     ///////////////////////////////////////////////////////////////////////////
     // Debugging and Stats
@@ -454,27 +453,54 @@
     Stats* stats() { return &fStats; }
     void dumpJSON(SkJSONWriter*) const;
 
+    /** Used to initialize a backend texture with either a constant color or from pixmaps. */
+    class BackendTextureData {
+    public:
+        enum class Type { kColor, kPixmaps };
+        BackendTextureData() = default;
+        BackendTextureData(const SkColor4f& color) : fType(Type::kColor), fColor(color) {}
+        BackendTextureData(const SkPixmap pixmaps[]) : fType(Type::kPixmaps), fPixmaps(pixmaps) {
+            SkASSERT(pixmaps);
+        }
+
+        Type type() const { return fType; }
+        SkColor4f color() const {
+            SkASSERT(this->type() == Type::kColor);
+            return fColor;
+        }
+
+        const SkPixmap& pixmap(int i) const { return fPixmaps[i]; }
+        const SkPixmap* pixmaps() const { return fPixmaps; }
+
+    private:
+        Type fType = Type::kColor;
+        union {
+            SkColor4f fColor = {0, 0, 0, 0};
+            const SkPixmap* fPixmaps;
+        };
+    };
+
     /**
      * Creates a texture directly in the backend API without wrapping it in a GrTexture.
      * Must be matched with a call to deleteBackendTexture().
      *
-     * If srcData is provided it will be used to initialize the texture. If srcData is
-     * not provided but a color is then it is used to initialize the texture. If neither
-     * srcData nor a color is provided then the texture is left uninitialized.
+     * numMipLevels must be 1 or be the number of levels for a complete MIP hierarchy with
+     * dimensions as the base size. Otherwise this will fail.
      *
-     * If srcData is provided and mipMapped is kYes then data for all the miplevels must be
-     * provided (or the method will fail). If only a color is provided and mipMapped is kYes
-     * then all the mip levels will be allocated and initialized to the color. If neither
-     * srcData nor a color is provided but mipMapped is kYes then the mip levels will be allocated
-     * but left uninitialized.
+     * If data is null the texture is uninitialized.
      *
-     * Note: if more than one pixmap is provided (i.e., for mipmap levels) they must all share
-     * the same SkColorType.
+     * If data represents a color then all texture levels are cleared to that color.
+     *
+     * If data represents pixmaps then it must have numMipLevels pixmaps and they must be sized
+     * correctly according to the MIP sizes implied by dimensions. They must all have the same color
+     * type and that color type must be compatible with the texture format.
      */
-    GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
-                                          GrMipMapped, GrRenderable,
-                                          const SkPixmap srcData[], int numMipLevels,
-                                          const SkColor4f* color, GrProtected isProtected);
+    GrBackendTexture createBackendTexture(SkISize dimensions,
+                                          const GrBackendFormat&,
+                                          GrRenderable,
+                                          const BackendTextureData* data,
+                                          int numMipLevels,
+                                          GrProtected isProtected);
 
     /**
      * Frees a texture created by createBackendTexture(). If ownership of the backend
@@ -542,23 +568,10 @@
         }
     }
 
-    /**
-     * Returns a key that represents the sampler that will be created for the passed in parameters.
-     * Currently this key is only used when we are building a vulkan pipeline with immutable
-     * samplers. In that case, we need our cache key to also contain this key.
-     *
-     * A return value of 0 indicates that the program/pipeline we are creating is not affected by
-     * the sampler.
-     */
-    virtual uint32_t getExtraSamplerKeyForProgram(const GrSamplerState&, const GrBackendFormat&) {
-        return 0;
-    }
-
     virtual void storeVkPipelineCacheData() {}
 
 protected:
-    static bool MipMapsAreCorrect(int baseWidth, int baseHeight, GrMipMapped,
-                                  const SkPixmap srcData[], int numMipLevels);
+    static bool MipMapsAreCorrect(SkISize, const BackendTextureData*, int numMipLevels);
 
     // Handles cases where a surface will be updated without a call to flushRenderTarget.
     void didWriteToSurface(GrSurface* surface, GrSurfaceOrigin origin, const SkIRect* bounds,
@@ -570,10 +583,12 @@
     sk_sp<const GrCaps>              fCaps;
 
 private:
-    virtual GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
-                                                    GrMipMapped, GrRenderable,
-                                                    const SkPixmap srcData[], int numMipLevels,
-                                                    const SkColor4f* color, GrProtected) = 0;
+    virtual GrBackendTexture onCreateBackendTexture(SkISize dimensions,
+                                                    const GrBackendFormat&,
+                                                    GrRenderable,
+                                                    const BackendTextureData*,
+                                                    int numMipLevels,
+                                                    GrProtected isProtected) = 0;
 
     // called when the 3D context state is unknown. Subclass should emit any
     // assumed 3D context state and dirty any state cache.
@@ -653,7 +668,7 @@
     virtual bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                                const SkIPoint& dstPoint) = 0;
 
-    virtual void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+    virtual bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                                const GrFlushInfo&, const GrPrepareForExternalIORequests&) = 0;
 
 #ifdef SK_ENABLE_DUMP_GPU
diff --git a/src/gpu/GrImageInfo.h b/src/gpu/GrImageInfo.h
index d49f0a1..12ebdb2 100644
--- a/src/gpu/GrImageInfo.h
+++ b/src/gpu/GrImageInfo.h
@@ -36,15 +36,15 @@
     GrImageInfo& operator=(const GrImageInfo&) = default;
     GrImageInfo& operator=(GrImageInfo&&) = default;
 
-    GrImageInfo makeColorType(GrColorType ct) {
+    GrImageInfo makeColorType(GrColorType ct) const {
         return {ct, this->alphaType(), this->refColorSpace(), this->width(), this->height()};
     }
 
-    GrImageInfo makeAlphaType(SkAlphaType at) {
+    GrImageInfo makeAlphaType(SkAlphaType at) const {
         return {this->colorType(), at, this->refColorSpace(), this->width(), this->height()};
     }
 
-    GrImageInfo makeWH(int width, int height) {
+    GrImageInfo makeWH(int width, int height) const {
         return {this->colorType(), this->alphaType(), this->refColorSpace(), width, height};
     }
 
diff --git a/src/gpu/GrLegacyDirectContext.cpp b/src/gpu/GrLegacyDirectContext.cpp
index 15746e9..1353001 100644
--- a/src/gpu/GrLegacyDirectContext.cpp
+++ b/src/gpu/GrLegacyDirectContext.cpp
@@ -122,6 +122,7 @@
     typedef GrContext INHERITED;
 };
 
+#ifdef SK_GL
 sk_sp<GrContext> GrContext::MakeGL(sk_sp<const GrGLInterface> interface) {
     GrContextOptions defaultOptions;
     return MakeGL(std::move(interface), defaultOptions);
@@ -150,6 +151,7 @@
     }
     return context;
 }
+#endif
 
 sk_sp<GrContext> GrContext::MakeMock(const GrMockOptions* mockOptions) {
     GrContextOptions defaultOptions;
diff --git a/src/gpu/GrOnFlushResourceProvider.cpp b/src/gpu/GrOnFlushResourceProvider.cpp
index 2cf4af8..8cb748f 100644
--- a/src/gpu/GrOnFlushResourceProvider.cpp
+++ b/src/gpu/GrOnFlushResourceProvider.cpp
@@ -50,7 +50,8 @@
     }
     auto task = static_cast<GrTextureResolveRenderTask*>(fDrawingMgr->fOnFlushRenderTasks.push_back(
             sk_make_sp<GrTextureResolveRenderTask>()).get());
-    task->addProxy(textureProxy, resolveFlags, *this->caps());
+    task->addProxy(GrSurfaceProxyView(textureProxy, textureProxy->origin(), GrSwizzle()),
+                   resolveFlags, *this->caps());
     task->makeClosed(*this->caps());
 }
 
diff --git a/src/gpu/GrOpFlushState.cpp b/src/gpu/GrOpFlushState.cpp
index 05e42e1..60ffa51 100644
--- a/src/gpu/GrOpFlushState.cpp
+++ b/src/gpu/GrOpFlushState.cpp
@@ -38,13 +38,13 @@
 
     GrPipeline::InitArgs pipelineArgs;
     pipelineArgs.fInputFlags = pipelineFlags;
-    pipelineArgs.fDstProxy = this->dstProxy();
+    pipelineArgs.fDstProxyView = this->dstProxyView();
     pipelineArgs.fCaps = &this->caps();
     pipelineArgs.fUserStencil = stencilSettings;
     pipelineArgs.fOutputSwizzle = this->drawOpArgs().outputSwizzle();
-    GrPipeline* pipeline = this->allocator()->make<GrPipeline>(pipelineArgs,
-                                                               std::move(processorSet),
-                                                               this->detachAppliedClip());
+    auto pipeline = this->allocator()->make<GrPipeline>(pipelineArgs,
+                                                        std::move(processorSet),
+                                                        this->detachAppliedClip());
 
     while (fCurrDraw != fDraws.end() && fCurrDraw->fOp == op) {
         GrDeferredUploadToken drawToken = fTokenTracker->nextTokenToFlush();
@@ -55,12 +55,15 @@
         }
 
         GrProgramInfo programInfo(this->proxy()->numSamples(),
-                                  this->proxy()->origin(),
-                                  *pipeline,
-                                  *fCurrDraw->fGeometryProcessor,
+                                  this->proxy()->numStencilSamples(),
+                                  this->proxy()->backendFormat(),
+                                  this->view()->origin(),
+                                  pipeline,
+                                  fCurrDraw->fGeometryProcessor,
                                   fCurrDraw->fFixedDynamicState,
                                   fCurrDraw->fDynamicStateArrays,
-                                  fCurrDraw->fMeshCnt);
+                                  fCurrDraw->fMeshCnt,
+                                  fCurrDraw->fPrimitiveType);
 
         this->opsRenderPass()->draw(programInfo, fCurrDraw->fMeshes,
                                     fCurrDraw->fMeshCnt, chainBounds);
@@ -138,9 +141,10 @@
 }
 
 void GrOpFlushState::recordDraw(
-        sk_sp<const GrGeometryProcessor> gp, const GrMesh meshes[], int meshCnt,
+        const GrGeometryProcessor* gp, const GrMesh meshes[], int meshCnt,
         const GrPipeline::FixedDynamicState* fixedDynamicState,
-        const GrPipeline::DynamicStateArrays* dynamicStateArrays) {
+        const GrPipeline::DynamicStateArrays* dynamicStateArrays,
+        GrPrimitiveType primitiveType) {
     SkASSERT(fOpArgs);
     SkDEBUGCODE(fOpArgs->validate());
     bool firstDraw = fDraws.begin() == fDraws.end();
@@ -157,12 +161,13 @@
             dynamicStateArrays->fPrimitiveProcessorTextures[i]->ref();
         }
     }
-    draw.fGeometryProcessor = std::move(gp);
+    draw.fGeometryProcessor = gp;
     draw.fFixedDynamicState = fixedDynamicState;
     draw.fDynamicStateArrays = dynamicStateArrays;
     draw.fMeshes = meshes;
     draw.fMeshCnt = meshCnt;
     draw.fOp = fOpArgs->op();
+    draw.fPrimitiveType = primitiveType;
     if (firstDraw) {
         fBaseDrawToken = token;
     }
diff --git a/src/gpu/GrOpFlushState.h b/src/gpu/GrOpFlushState.h
index 27dac24..b934097 100644
--- a/src/gpu/GrOpFlushState.h
+++ b/src/gpu/GrOpFlushState.h
@@ -15,6 +15,7 @@
 #include "src/gpu/GrBufferAllocPool.h"
 #include "src/gpu/GrDeferredUpload.h"
 #include "src/gpu/GrRenderTargetProxy.h"
+#include "src/gpu/GrSurfaceProxyView.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
 
 class GrGpu;
@@ -57,37 +58,40 @@
 
     /** Additional data required on a per-op basis when executing GrOps. */
     struct OpArgs {
-        explicit OpArgs(GrOp* op, GrRenderTargetProxy* proxy, GrAppliedClip* appliedClip,
-                        const GrXferProcessor::DstProxy& dstProxy)
-            : fOp(op)
-            , fProxy(proxy)
-            , fAppliedClip(appliedClip)
-            , fDstProxy(dstProxy) {
+        // TODO: why does OpArgs have the op we're going to pass it to as a member? Remove it.
+        explicit OpArgs(GrOp* op, GrSurfaceProxyView* surfaceView, GrAppliedClip* appliedClip,
+                        const GrXferProcessor::DstProxyView& dstProxyView)
+                : fOp(op)
+                , fSurfaceView(surfaceView)
+                , fRenderTargetProxy(surfaceView->asRenderTargetProxy())
+                , fAppliedClip(appliedClip)
+                , fDstProxyView(dstProxyView) {
+            SkASSERT(surfaceView->asRenderTargetProxy());
         }
 
-        int numSamples() const { return fProxy->numSamples(); }
-        GrSurfaceOrigin origin() const { return fProxy->origin(); }
-        GrSwizzle outputSwizzle() const { return fProxy->outputSwizzle(); }
+        GrSurfaceOrigin origin() const { return fSurfaceView->origin(); }
+        GrSwizzle outputSwizzle() const { return fSurfaceView->swizzle(); }
 
         GrOp* op() { return fOp; }
-        GrRenderTargetProxy* proxy() const { return fProxy; }
-        GrRenderTarget* renderTarget() const { return fProxy->peekRenderTarget(); }
+        const GrSurfaceProxyView* view() const { return fSurfaceView; }
+        GrRenderTargetProxy* proxy() const { return fRenderTargetProxy; }
         GrAppliedClip* appliedClip() { return fAppliedClip; }
         const GrAppliedClip* appliedClip() const { return fAppliedClip; }
-        const GrXferProcessor::DstProxy& dstProxy() const { return fDstProxy; }
+        const GrXferProcessor::DstProxyView& dstProxyView() const { return fDstProxyView; }
 
 #ifdef SK_DEBUG
         void validate() const {
             SkASSERT(fOp);
-            SkASSERT(fProxy);
+            SkASSERT(fSurfaceView);
         }
 #endif
 
     private:
-        GrOp*                     fOp;
-        GrRenderTargetProxy*      fProxy;
-        GrAppliedClip*            fAppliedClip;
-        GrXferProcessor::DstProxy fDstProxy;     // TODO: do we still need the dst proxy here?
+        GrOp*                         fOp;
+        GrSurfaceProxyView*           fSurfaceView;
+        GrRenderTargetProxy*          fRenderTargetProxy;
+        GrAppliedClip*                fAppliedClip;
+        GrXferProcessor::DstProxyView fDstProxyView;   // TODO: do we still need the dst proxy here?
     };
 
     void setOpArgs(OpArgs* opArgs) { fOpArgs = opArgs; }
@@ -98,11 +102,11 @@
         return *fOpArgs;
     }
 
-    void setSampledProxyArray(SkTArray<GrTextureProxy*, true>* sampledProxies) {
+    void setSampledProxyArray(SkTArray<GrSurfaceProxy*, true>* sampledProxies) {
         fSampledProxies = sampledProxies;
     }
 
-    SkTArray<GrTextureProxy*, true>* sampledProxyArray() override {
+    SkTArray<GrSurfaceProxy*, true>* sampledProxyArray() override {
         return fSampledProxies;
     }
 
@@ -113,9 +117,9 @@
     GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&&) final;
 
     /** Overrides of GrMeshDrawOp::Target. */
-    void recordDraw(sk_sp<const GrGeometryProcessor>, const GrMesh[], int meshCnt,
+    void recordDraw(const GrGeometryProcessor*, const GrMesh[], int meshCnt,
                     const GrPipeline::FixedDynamicState*,
-                    const GrPipeline::DynamicStateArrays*) final;
+                    const GrPipeline::DynamicStateArrays*, GrPrimitiveType) final;
     void* makeVertexSpace(size_t vertexSize, int vertexCount, sk_sp<const GrBuffer>*,
                           int* startVertex) final;
     uint16_t* makeIndexSpace(int indexCount, sk_sp<const GrBuffer>*, int* startIndex) final;
@@ -127,10 +131,13 @@
                                     int* actualIndexCount) final;
     void putBackIndices(int indexCount) final;
     void putBackVertices(int vertices, size_t vertexStride) final;
-    GrRenderTargetProxy* proxy() const final { return fOpArgs->proxy(); }
-    const GrAppliedClip* appliedClip() final { return fOpArgs->appliedClip(); }
+    const GrSurfaceProxyView* view() const { return this->drawOpArgs().view(); }
+    GrRenderTargetProxy* proxy() const final { return this->drawOpArgs().proxy(); }
+    const GrAppliedClip* appliedClip() const final { return this->drawOpArgs().appliedClip(); }
     GrAppliedClip detachAppliedClip() final;
-    const GrXferProcessor::DstProxy& dstProxy() const final { return fOpArgs->dstProxy(); }
+    const GrXferProcessor::DstProxyView& dstProxyView() const final {
+        return this->drawOpArgs().dstProxyView();
+    }
     GrDeferredUploadTarget* deferredUploadTarget() final { return this; }
     const GrCaps& caps() const final;
     GrResourceProvider* resourceProvider() const final { return fResourceProvider; }
@@ -158,12 +165,16 @@
     // the shared state once and then issue draws for each mesh.
     struct Draw {
         ~Draw();
-        sk_sp<const GrGeometryProcessor> fGeometryProcessor;
-        const GrPipeline::FixedDynamicState* fFixedDynamicState;
-        const GrPipeline::DynamicStateArrays* fDynamicStateArrays;
+        // The geometry processor is always forced to be in an arena allocation or appears on
+        // the stack (for CCPR). In either case this object does not need to manage its
+        // lifetime.
+        const GrGeometryProcessor* fGeometryProcessor = nullptr;
+        const GrPipeline::FixedDynamicState* fFixedDynamicState = nullptr;
+        const GrPipeline::DynamicStateArrays* fDynamicStateArrays = nullptr;
         const GrMesh* fMeshes = nullptr;
         const GrOp* fOp = nullptr;
         int fMeshCnt = 0;
+        GrPrimitiveType fPrimitiveType;
     };
 
     // Storage for ops' pipelines, draws, and inline uploads.
@@ -188,7 +199,7 @@
 
     // This field is only transiently set during flush. Each GrOpsTask will set it to point to an
     // array of proxies it uses before call onPrepare and onExecute.
-    SkTArray<GrTextureProxy*, true>* fSampledProxies;
+    SkTArray<GrSurfaceProxy*, true>* fSampledProxies;
 
     GrGpu* fGpu;
     GrResourceProvider* fResourceProvider;
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index 4306ad0..b57066f 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -35,7 +35,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-using DstProxy = GrXferProcessor::DstProxy;
+using DstProxyView = GrXferProcessor::DstProxyView;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -123,13 +123,13 @@
 
 GrOpsTask::OpChain::OpChain(std::unique_ptr<GrOp> op,
                             GrProcessorSet::Analysis processorAnalysis,
-                            GrAppliedClip* appliedClip, const DstProxy* dstProxy)
+                            GrAppliedClip* appliedClip, const DstProxyView* dstProxyView)
         : fList{std::move(op)}
         , fProcessorAnalysis(processorAnalysis)
         , fAppliedClip(appliedClip) {
     if (fProcessorAnalysis.requiresDstTexture()) {
-        SkASSERT(dstProxy && dstProxy->proxy());
-        fDstProxy = *dstProxy;
+        SkASSERT(dstProxyView && dstProxyView->proxy());
+        fDstProxyView = *dstProxyView;
     }
     fBounds = fList.head()->bounds();
 }
@@ -141,8 +141,8 @@
     for (const auto& op : GrOp::ChainRange<>(fList.head())) {
         op.visitProxies(func);
     }
-    if (fDstProxy.proxy()) {
-        func(fDstProxy.proxy(), GrMipMapped::kNo);
+    if (fDstProxyView.proxy()) {
+        func(fDstProxyView.proxy(), GrMipMapped::kNo);
     }
     if (fAppliedClip) {
         fAppliedClip->visitProxies(func);
@@ -234,13 +234,13 @@
 // Attempts to concatenate the given chain onto our own and merge ops across the chains. Returns
 // whether the operation succeeded. On success, the provided list will be returned empty.
 bool GrOpsTask::OpChain::tryConcat(
-        List* list, GrProcessorSet::Analysis processorAnalysis, const DstProxy& dstProxy,
+        List* list, GrProcessorSet::Analysis processorAnalysis, const DstProxyView& dstProxyView,
         const GrAppliedClip* appliedClip, const SkRect& bounds, const GrCaps& caps,
         GrOpMemoryPool* pool, GrAuditTrail* auditTrail) {
     SkASSERT(!fList.empty());
     SkASSERT(!list->empty());
-    SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxy.proxy()));
-    SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxy.proxy()));
+    SkASSERT(fProcessorAnalysis.requiresDstTexture() == SkToBool(fDstProxyView.proxy()));
+    SkASSERT(processorAnalysis.requiresDstTexture() == SkToBool(dstProxyView.proxy()));
     // All returns use explicit tuple constructor rather than {a, b} to work around old GCC bug.
     if (fList.head()->classID() != list->head()->classID() ||
         SkToBool(fAppliedClip) != SkToBool(appliedClip) ||
@@ -253,7 +253,7 @@
                 // chain nor combine overlapping Ops.
                 GrRectsTouchOrOverlap(fBounds, bounds)) ||
         (fProcessorAnalysis.requiresDstTexture() != processorAnalysis.requiresDstTexture()) ||
-        (fProcessorAnalysis.requiresDstTexture() && fDstProxy != dstProxy)) {
+        (fProcessorAnalysis.requiresDstTexture() && fDstProxyView != dstProxyView)) {
         return false;
     }
 
@@ -293,8 +293,8 @@
 
 bool GrOpsTask::OpChain::prependChain(OpChain* that, const GrCaps& caps, GrOpMemoryPool* pool,
                                       GrAuditTrail* auditTrail) {
-    if (!that->tryConcat(
-            &fList, fProcessorAnalysis, fDstProxy, fAppliedClip, fBounds, caps, pool, auditTrail)) {
+    if (!that->tryConcat(&fList, fProcessorAnalysis, fDstProxyView, fAppliedClip, fBounds, caps,
+                         pool, auditTrail)) {
         this->validate();
         // append failed
         return false;
@@ -305,7 +305,7 @@
     fList = std::move(that->fList);
     fBounds = that->fBounds;
 
-    that->fDstProxy.setProxy(nullptr);
+    that->fDstProxyView.setProxyView({});
     if (that->fAppliedClip) {
         for (int i = 0; i < that->fAppliedClip->numClipCoverageFragmentProcessors(); ++i) {
             that->fAppliedClip->detachClipCoverageFragmentProcessor(i);
@@ -317,17 +317,18 @@
 
 std::unique_ptr<GrOp> GrOpsTask::OpChain::appendOp(
         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis,
-        const DstProxy* dstProxy, const GrAppliedClip* appliedClip, const GrCaps& caps,
+        const DstProxyView* dstProxyView, const GrAppliedClip* appliedClip, const GrCaps& caps,
         GrOpMemoryPool* pool, GrAuditTrail* auditTrail) {
-    const GrXferProcessor::DstProxy noDstProxy;
-    if (!dstProxy) {
-        dstProxy = &noDstProxy;
+    const GrXferProcessor::DstProxyView noDstProxyView;
+    if (!dstProxyView) {
+        dstProxyView = &noDstProxyView;
     }
     SkASSERT(op->isChainHead() && op->isChainTail());
     SkRect opBounds = op->bounds();
     List chain(std::move(op));
     if (!this->tryConcat(
-            &chain, processorAnalysis, *dstProxy, appliedClip, opBounds, caps, pool, auditTrail)) {
+            &chain, processorAnalysis, *dstProxyView, appliedClip, opBounds, caps, pool,
+            auditTrail)) {
         // append failed, give the op back to the caller.
         this->validate();
         return chain.popHead();
@@ -352,15 +353,15 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GrOpsTask::GrOpsTask(sk_sp<GrOpMemoryPool> opMemoryPool,
-                     sk_sp<GrRenderTargetProxy> rtProxy,
+                     GrSurfaceProxyView view,
                      GrAuditTrail* auditTrail)
-        : GrRenderTask(std::move(rtProxy))
+        : GrRenderTask(std::move(view))
         , fOpMemoryPool(std::move(opMemoryPool))
         , fAuditTrail(auditTrail)
         , fLastClipStackGenID(SK_InvalidUniqueID)
         SkDEBUGCODE(, fNumClips(0)) {
     SkASSERT(fOpMemoryPool);
-    fTarget->setLastRenderTask(this);
+    fTargetView.proxy()->setLastRenderTask(this);
 }
 
 void GrOpsTask::deleteOps() {
@@ -381,11 +382,12 @@
     this->deleteOps();
     fClipAllocator.reset();
 
-    if (fTarget && this == fTarget->getLastRenderTask()) {
-        fTarget->setLastRenderTask(nullptr);
+    GrSurfaceProxy* proxy = fTargetView.proxy();
+    if (proxy && this == proxy->getLastRenderTask()) {
+        proxy->setLastRenderTask(nullptr);
     }
 
-    fTarget.reset();
+    fTargetView.reset();
     fDeferredProxies.reset();
     fSampledProxies.reset();
     fAuditTrail = nullptr;
@@ -406,13 +408,16 @@
 
     for (const auto& chain : fOpChains) {
         if (chain.shouldExecute()) {
-            chain.head()->prePrepare(context, chain.appliedClip());
+            chain.head()->prePrepare(context,
+                                     &fTargetView,
+                                     chain.appliedClip(),
+                                     chain.dstProxyView());
         }
     }
 }
 
 void GrOpsTask::onPrepare(GrOpFlushState* flushState) {
-    SkASSERT(fTarget->peekRenderTarget());
+    SkASSERT(fTargetView.proxy()->peekRenderTarget());
     SkASSERT(this->isClosed());
 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
@@ -433,11 +438,17 @@
             TRACE_EVENT0("skia.gpu", chain.head()->name());
 #endif
             GrOpFlushState::OpArgs opArgs(chain.head(),
-                                          fTarget->asRenderTargetProxy(),
+                                          &fTargetView,
                                           chain.appliedClip(),
-                                          chain.dstProxy());
+                                          chain.dstProxyView());
 
             flushState->setOpArgs(&opArgs);
+
+            // Temporary debugging helper: for debugging prePrepare w/o going through DDLs
+            // Delete once most of the GrOps have an onPrePrepare.
+            // chain.head()->prePrepare(flushState->gpu()->getContext(), &fTargetView,
+            //                          chain.appliedClip());
+
             // GrOp::prePrepare may or may not have been called at this point
             chain.head()->prepare(flushState);
             flushState->setOpArgs(nullptr);
@@ -449,7 +460,7 @@
 static GrOpsRenderPass* create_render_pass(
         GrGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
         GrLoadOp colorLoadOp, const SkPMColor4f& loadClearColor, GrLoadOp stencilLoadOp,
-        GrStoreOp stencilStoreOp, const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+        GrStoreOp stencilStoreOp, const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     const GrOpsRenderPass::LoadAndStoreInfo kColorLoadStoreInfo {
         colorLoadOp,
         GrStoreOp::kStore,
@@ -482,7 +493,9 @@
         return false;
     }
 
-    SkASSERT(fTarget->peekRenderTarget());
+    SkASSERT(fTargetView.proxy());
+    GrRenderTargetProxy* proxy = fTargetView.proxy()->asRenderTargetProxy();
+    SkASSERT(proxy);
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
 
     // Make sure load ops are not kClear if the GPU needs to use draws for clears
@@ -490,9 +503,20 @@
              !flushState->gpu()->caps()->performColorClearsAsDraws());
 
     const GrCaps& caps = *flushState->gpu()->caps();
-    GrRenderTarget* renderTarget = fTarget.get()->peekRenderTarget();
+    GrRenderTarget* renderTarget = proxy->peekRenderTarget();
     SkASSERT(renderTarget);
-    GrStencilAttachment* stencil = renderTarget->renderTargetPriv().getStencilAttachment();
+
+    GrStencilAttachment* stencil = nullptr;
+    if (int numStencilSamples = proxy->numStencilSamples()) {
+        if (!flushState->resourceProvider()->attachStencilAttachment(
+                renderTarget, numStencilSamples)) {
+            SkDebugf("WARNING: failed to attach a stencil buffer. Rendering will be skipped.\n");
+            return false;
+        }
+        stencil = renderTarget->renderTargetPriv().getStencilAttachment();
+    }
+
+    SkASSERT(!stencil || stencil->numSamples() == proxy->numStencilSamples());
 
     GrLoadOp stencilLoadOp;
     switch (fInitialStencilContent) {
@@ -536,9 +560,12 @@
             : GrStoreOp::kStore;
 
     GrOpsRenderPass* renderPass = create_render_pass(
-            flushState->gpu(), fTarget->peekRenderTarget(), fTarget->origin(),
+            flushState->gpu(), proxy->peekRenderTarget(), fTargetView.origin(),
             fClippedContentBounds, fColorLoadOp, fLoadClearColor, stencilLoadOp, stencilStoreOp,
             fSampledProxies);
+    if (!renderPass) {
+        return false;
+    }
     flushState->setOpsRenderPass(renderPass);
     renderPass->begin();
 
@@ -552,9 +579,9 @@
 #endif
 
         GrOpFlushState::OpArgs opArgs(chain.head(),
-                                      fTarget->asRenderTargetProxy(),
+                                      &fTargetView,
                                       chain.appliedClip(),
-                                      chain.dstProxy());
+                                      chain.dstProxyView());
 
         flushState->setOpArgs(&opArgs);
         chain.head()->execute(flushState, chain.bounds());
@@ -572,7 +599,9 @@
     fColorLoadOp = op;
     fLoadClearColor = color;
     if (GrLoadOp::kClear == fColorLoadOp) {
-        fTotalBounds = fTarget->getBoundsRect();
+        GrSurfaceProxy* proxy = fTargetView.proxy();
+        SkASSERT(proxy);
+        fTotalBounds = proxy->getBoundsRect();
     }
 }
 
@@ -591,7 +620,7 @@
         // If the opsTask is using a render target which wraps a vulkan command buffer, we can't do
         // a clear load since we cannot change the render pass that we are using. Thus we fall back
         // to making a clear op in this case.
-        return !fTarget->asRenderTargetProxy()->wrapsVkSecondaryCB();
+        return !fTargetView.asRenderTargetProxy()->wrapsVkSecondaryCB();
     }
 
     // Could not empty the task, so an op must be added to handle the clear
@@ -661,8 +690,8 @@
     }
 }
 
-void GrOpsTask::visitProxies_debugOnly(const VisitSurfaceProxyFunc& func) const {
-    auto textureFunc = [ func ] (GrTextureProxy* tex, GrMipMapped mipmapped) {
+void GrOpsTask::visitProxies_debugOnly(const GrOp::VisitProxyFunc& func) const {
+    auto textureFunc = [ func ] (GrSurfaceProxy* tex, GrMipMapped mipmapped) {
         func(tex, mipmapped);
     };
 
@@ -717,24 +746,26 @@
         alloc->addInterval(fDeferredProxies[i], 0, 0, GrResourceAllocator::ActualUse::kNo);
     }
 
+    GrSurfaceProxy* targetProxy = fTargetView.proxy();
+
     // Add the interval for all the writes to this GrOpsTasks's target
     if (fOpChains.count()) {
         unsigned int cur = alloc->curOp();
 
-        alloc->addInterval(fTarget.get(), cur, cur + fOpChains.count() - 1,
+        alloc->addInterval(targetProxy, cur, cur + fOpChains.count() - 1,
                            GrResourceAllocator::ActualUse::kYes);
     } else {
         // This can happen if there is a loadOp (e.g., a clear) but no other draws. In this case we
         // still need to add an interval for the destination so we create a fake op# for
         // the missing clear op.
-        alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
+        alloc->addInterval(targetProxy, alloc->curOp(), alloc->curOp(),
                            GrResourceAllocator::ActualUse::kYes);
         alloc->incOps();
     }
 
     auto gather = [ alloc SkDEBUGCODE(, this) ] (GrSurfaceProxy* p, GrMipMapped) {
         alloc->addInterval(p, alloc->curOp(), alloc->curOp(), GrResourceAllocator::ActualUse::kYes
-                           SkDEBUGCODE(, fTarget.get() == p));
+                           SkDEBUGCODE(, fTargetView.proxy() == p));
     };
     for (const OpChain& recordedOp : fOpChains) {
         recordedOp.visitProxies(gather);
@@ -747,10 +778,11 @@
 
 void GrOpsTask::recordOp(
         std::unique_ptr<GrOp> op, GrProcessorSet::Analysis processorAnalysis, GrAppliedClip* clip,
-        const DstProxy* dstProxy, const GrCaps& caps) {
+        const DstProxyView* dstProxyView, const GrCaps& caps) {
     SkDEBUGCODE(op->validate();)
-    SkASSERT(processorAnalysis.requiresDstTexture() == (dstProxy && dstProxy->proxy()));
-    SkASSERT(fTarget);
+    SkASSERT(processorAnalysis.requiresDstTexture() == (dstProxyView && dstProxyView->proxy()));
+    GrSurfaceProxy* proxy = fTargetView.proxy();
+    SkASSERT(proxy);
 
     // A closed GrOpsTask should never receive new/more ops
     SkASSERT(!this->isClosed());
@@ -767,7 +799,7 @@
     // 1) check every op
     // 2) intersect with something
     // 3) find a 'blocker'
-    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), fTarget->uniqueID());
+    GR_AUDIT_TRAIL_ADD_OP(fAuditTrail, op.get(), proxy->uniqueID());
     GrOP_INFO("opsTask: %d Recording (%s, opID: %u)\n"
               "\tBounds [L: %.2f, T: %.2f R: %.2f B: %.2f]\n",
                this->uniqueID(),
@@ -782,7 +814,7 @@
         int i = 0;
         while (true) {
             OpChain& candidate = fOpChains.fromBack(i);
-            op = candidate.appendOp(std::move(op), processorAnalysis, dstProxy, clip, caps,
+            op = candidate.appendOp(std::move(op), processorAnalysis, dstProxyView, clip, caps,
                                     fOpMemoryPool.get(), fAuditTrail);
             if (!op) {
                 return;
@@ -805,7 +837,7 @@
         clip = fClipAllocator.make<GrAppliedClip>(std::move(*clip));
         SkDEBUGCODE(fNumClips++;)
     }
-    fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxy);
+    fOpChains.emplace_back(std::move(op), processorAnalysis, clip, dstProxyView);
 }
 
 void GrOpsTask::forwardCombine(const GrCaps& caps) {
@@ -843,9 +875,10 @@
         const GrCaps& caps, SkIRect* targetUpdateBounds) {
     this->forwardCombine(caps);
     if (!this->isNoOp()) {
-        SkRect clippedContentBounds = fTarget->getBoundsRect();
-        // TODO: If we can fix up GLPrograms test to always intersect the fTarget bounds then we can
-        // simply assert here that the bounds intersect.
+        GrSurfaceProxy* proxy = fTargetView.proxy();
+        SkRect clippedContentBounds = proxy->getBoundsRect();
+        // TODO: If we can fix up GLPrograms test to always intersect the fTargetView proxy bounds
+        // then we can simply assert here that the bounds intersect.
         if (clippedContentBounds.intersect(fTotalBounds)) {
             clippedContentBounds.roundOut(&fClippedContentBounds);
             *targetUpdateBounds = fClippedContentBounds;
diff --git a/src/gpu/GrOpsTask.h b/src/gpu/GrOpsTask.h
index c223812..a1b2ed1 100644
--- a/src/gpu/GrOpsTask.h
+++ b/src/gpu/GrOpsTask.h
@@ -35,10 +35,10 @@
 
 class GrOpsTask : public GrRenderTask {
 private:
-    using DstProxy = GrXferProcessor::DstProxy;
+    using DstProxyView = GrXferProcessor::DstProxyView;
 
 public:
-    GrOpsTask(sk_sp<GrOpMemoryPool>, sk_sp<GrRenderTargetProxy>, GrAuditTrail*);
+    GrOpsTask(sk_sp<GrOpMemoryPool>, GrSurfaceProxyView, GrAuditTrail*);
     ~GrOpsTask() override;
 
     GrOpsTask* asOpsTask() override { return this; }
@@ -58,14 +58,20 @@
     void onPrepare(GrOpFlushState* flushState) override;
     bool onExecute(GrOpFlushState* flushState) override;
 
-    void addSampledTexture(GrTextureProxy* proxy) {
+    void addSampledTexture(GrSurfaceProxy* proxy) {
+        // This function takes a GrSurfaceProxy because all subsequent uses of the proxy do not
+        // require the specifics of GrTextureProxy, so this avoids a number of unnecessary virtual
+        // asTextureProxy() calls. However, sampling the proxy implicitly requires that the proxy
+        // be a texture. Eventually, when proxies are a unified type with flags, this can just
+        // assert that capability.
+        SkASSERT(proxy->asTextureProxy());
         fSampledProxies.push_back(proxy);
     }
 
     void addOp(std::unique_ptr<GrOp> op, GrTextureResolveManager textureResolveManager,
                const GrCaps& caps) {
         auto addDependency = [ textureResolveManager, &caps, this ] (
-                GrTextureProxy* p, GrMipMapped mipmapped) {
+                GrSurfaceProxy* p, GrMipMapped mipmapped) {
             this->addDependency(p, mipmapped, textureResolveManager, caps);
         };
 
@@ -81,30 +87,35 @@
     }
 
     void addDrawOp(std::unique_ptr<GrDrawOp> op, const GrProcessorSet::Analysis& processorAnalysis,
-                   GrAppliedClip&& clip, const DstProxy& dstProxy,
+                   GrAppliedClip&& clip, const DstProxyView& dstProxyView,
                    GrTextureResolveManager textureResolveManager, const GrCaps& caps) {
         auto addDependency = [ textureResolveManager, &caps, this ] (
-                GrTextureProxy* p, GrMipMapped mipmapped) {
+                GrSurfaceProxy* p, GrMipMapped mipmapped) {
             this->addSampledTexture(p);
             this->addDependency(p, mipmapped, textureResolveManager, caps);
         };
 
         op->visitProxies(addDependency);
         clip.visitProxies(addDependency);
-        if (dstProxy.proxy()) {
-            this->addSampledTexture(dstProxy.proxy());
-            addDependency(dstProxy.proxy(), GrMipMapped::kNo);
+        if (dstProxyView.proxy()) {
+            this->addSampledTexture(dstProxyView.proxy());
+            addDependency(dstProxyView.proxy(), GrMipMapped::kNo);
         }
 
         this->recordOp(std::move(op), processorAnalysis, clip.doesClip() ? &clip : nullptr,
-                       &dstProxy, caps);
+                       &dstProxyView, caps);
     }
 
     void discard();
 
     SkDEBUGCODE(void dump(bool printDependencies) const override;)
     SkDEBUGCODE(int numClips() const override { return fNumClips; })
-    SkDEBUGCODE(void visitProxies_debugOnly(const VisitSurfaceProxyFunc&) const override;)
+    SkDEBUGCODE(void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const override;)
+
+#if GR_TEST_UTILS
+    int numOpChains() const { return fOpChains.count(); }
+    const GrOp* getChain(int index) const { return fOpChains[index].head(); }
+#endif
 
 private:
     bool isNoOp() const {
@@ -164,7 +175,8 @@
     public:
         OpChain(const OpChain&) = delete;
         OpChain& operator=(const OpChain&) = delete;
-        OpChain(std::unique_ptr<GrOp>, GrProcessorSet::Analysis, GrAppliedClip*, const DstProxy*);
+        OpChain(std::unique_ptr<GrOp>, GrProcessorSet::Analysis, GrAppliedClip*,
+                const DstProxyView*);
 
         ~OpChain() {
             // The ops are stored in a GrMemoryPool and must be explicitly deleted via the pool.
@@ -176,7 +188,7 @@
         GrOp* head() const { return fList.head(); }
 
         GrAppliedClip* appliedClip() const { return fAppliedClip; }
-        const DstProxy& dstProxy() const { return fDstProxy; }
+        const DstProxyView& dstProxyView() const { return fDstProxyView; }
         const SkRect& bounds() const { return fBounds; }
 
         // Deletes all the ops in the chain via the pool.
@@ -191,7 +203,7 @@
         // 'op' to the caller upon failure, otherwise null. Fails when the op and chain aren't of
         // the same op type, have different clips or dst proxies.
         std::unique_ptr<GrOp> appendOp(std::unique_ptr<GrOp> op, GrProcessorSet::Analysis,
-                                       const DstProxy*, const GrAppliedClip*, const GrCaps&,
+                                       const DstProxyView*, const GrAppliedClip*, const GrCaps&,
                                        GrOpMemoryPool*, GrAuditTrail*);
 
         void setSkipExecuteFlag() { fSkipExecute = true; }
@@ -225,13 +237,13 @@
 
         void validate() const;
 
-        bool tryConcat(List*, GrProcessorSet::Analysis, const DstProxy&, const GrAppliedClip*,
+        bool tryConcat(List*, GrProcessorSet::Analysis, const DstProxyView&, const GrAppliedClip*,
                        const SkRect& bounds, const GrCaps&, GrOpMemoryPool*, GrAuditTrail*);
         static List DoConcat(List, List, const GrCaps&, GrOpMemoryPool*, GrAuditTrail*);
 
         List fList;
         GrProcessorSet::Analysis fProcessorAnalysis;
-        DstProxy fDstProxy;
+        DstProxyView fDstProxyView;
         GrAppliedClip* fAppliedClip;
         SkRect fBounds;
 
@@ -247,8 +259,8 @@
 
     void gatherProxyIntervals(GrResourceAllocator*) const override;
 
-    void recordOp(std::unique_ptr<GrOp>, GrProcessorSet::Analysis, GrAppliedClip*, const DstProxy*,
-                  const GrCaps& caps);
+    void recordOp(std::unique_ptr<GrOp>, GrProcessorSet::Analysis, GrAppliedClip*,
+                  const DstProxyView*, const GrCaps& caps);
 
     void forwardCombine(const GrCaps&);
 
@@ -290,7 +302,7 @@
 
     // TODO: We could look into this being a set if we find we're adding a lot of duplicates that is
     // causing slow downs.
-    SkTArray<GrTextureProxy*, true> fSampledProxies;
+    SkTArray<GrSurfaceProxy*, true> fSampledProxies;
 
     SkRect fTotalBounds = SkRect::MakeEmpty();
     SkIRect fClippedContentBounds = SkIRect::MakeEmpty();
diff --git a/src/gpu/GrPaint.cpp b/src/gpu/GrPaint.cpp
index bc7f538..cb8d479 100644
--- a/src/gpu/GrPaint.cpp
+++ b/src/gpu/GrPaint.cpp
@@ -35,16 +35,10 @@
     this->setXPFactory(GrCoverageSetOpXPFactory::Get(regionOp, invertCoverage));
 }
 
-void GrPaint::addColorTextureProcessor(sk_sp<GrTextureProxy> proxy, GrColorType srcColorType,
-                                       const SkMatrix& matrix) {
-    this->addColorFragmentProcessor(GrSimpleTextureEffect::Make(std::move(proxy), srcColorType,
-                                                                matrix));
-}
-
-void GrPaint::addColorTextureProcessor(sk_sp<GrTextureProxy> proxy, GrColorType srcColorType,
+void GrPaint::addColorTextureProcessor(sk_sp<GrTextureProxy> proxy, SkAlphaType alphaType,
                                        const SkMatrix& matrix, const GrSamplerState& samplerState) {
-    this->addColorFragmentProcessor(GrSimpleTextureEffect::Make(std::move(proxy), srcColorType,
-                                                                matrix, samplerState));
+    this->addColorFragmentProcessor(
+            GrSimpleTextureEffect::Make(std::move(proxy), alphaType, matrix, samplerState));
 }
 
 bool GrPaint::isConstantBlendedColor(SkPMColor4f* constantColor) const {
diff --git a/src/gpu/GrPaint.h b/src/gpu/GrPaint.h
index c2520b0..a13dc82 100644
--- a/src/gpu/GrPaint.h
+++ b/src/gpu/GrPaint.h
@@ -81,9 +81,8 @@
      * Helpers for adding color or coverage effects that sample a texture. The matrix is applied
      * to the src space position to compute texture coordinates.
      */
-    void addColorTextureProcessor(sk_sp<GrTextureProxy>, GrColorType srcColorType, const SkMatrix&);
-    void addColorTextureProcessor(sk_sp<GrTextureProxy>, GrColorType srcColorType, const SkMatrix&,
-                                  const GrSamplerState&);
+    void addColorTextureProcessor(sk_sp<GrTextureProxy>, SkAlphaType, const SkMatrix&,
+                                  const GrSamplerState& = GrSamplerState::ClampBilerp());
 
     int numColorFragmentProcessors() const { return fColorFragmentProcessors.count(); }
     int numCoverageFragmentProcessors() const { return fCoverageFragmentProcessors.count(); }
diff --git a/src/gpu/GrPathProcessor.cpp b/src/gpu/GrPathProcessor.cpp
index 861ff1b..f944e23 100644
--- a/src/gpu/GrPathProcessor.cpp
+++ b/src/gpu/GrPathProcessor.cpp
@@ -10,7 +10,9 @@
 #include "include/private/SkTo.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/gl/GrGLGpu.h"
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLVaryingHandler.h"
+#endif
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLPrimitiveProcessor.h"
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
@@ -56,33 +58,32 @@
 
     void emitTransforms(GrGLSLVaryingHandler* varyingHandler,
                         FPCoordTransformHandler* transformHandler) {
-        int i = 0;
-        while (const GrCoordTransform* coordTransform = transformHandler->nextCoordTransform()) {
+        for (int i = 0; *transformHandler; ++*transformHandler, ++i) {
+            auto [coordTransform, fp] = transformHandler->get();
             GrSLType varyingType =
-                    coordTransform->getMatrix().hasPerspective() ? kHalf3_GrSLType
-                                                                 : kHalf2_GrSLType;
+                    coordTransform.matrix().hasPerspective() ? kHalf3_GrSLType : kHalf2_GrSLType;
 
             SkString strVaryingName;
             strVaryingName.printf("TransformedCoord_%d", i);
             GrGLSLVarying v(varyingType);
+#ifdef SK_GL
             GrGLVaryingHandler* glVaryingHandler = (GrGLVaryingHandler*) varyingHandler;
             fInstalledTransforms.push_back().fHandle =
-                    glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(),
-                                                               &v).toIndex();
+                    glVaryingHandler->addPathProcessingVarying(strVaryingName.c_str(), &v).toIndex();
+#endif
             fInstalledTransforms.back().fType = varyingType;
 
             transformHandler->specifyCoordsForCurrCoordTransform(
-                                                        matrix_to_sksl(coordTransform->getMatrix()),
-                                                        UniformHandle(),
-                                                        GrShaderVar(SkString(v.fsIn()),
-                                                                             varyingType));
+                    matrix_to_sksl(coordTransform.matrix()),
+                    UniformHandle(),
+                    GrShaderVar(SkString(v.fsIn()), varyingType));
             ++i;
         }
     }
 
     void setData(const GrGLSLProgramDataManager& pd,
                  const GrPrimitiveProcessor& primProc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const GrPathProcessor& pathProc = primProc.cast<GrPathProcessor>();
         if (pathProc.color() != fColor) {
             pd.set4fv(fColorUniform, 1, pathProc.color().vec());
@@ -90,9 +91,14 @@
         }
 
         int t = 0;
-        while (const GrCoordTransform* coordTransform = transformIter.next()) {
+        for (auto [transform, fp] : transformRange) {
             SkASSERT(fInstalledTransforms[t].fHandle.isValid());
-            const SkMatrix& m = GetTransformMatrix(pathProc.localMatrix(), *coordTransform);
+            SkMatrix m;
+            if (fp.coordTransformsApplyToLocalCoords()) {
+                m = GetTransformMatrix(transform, pathProc.localMatrix());
+            } else {
+                m = GetTransformMatrix(transform, SkMatrix::I());
+            }
             if (fInstalledTransforms[t].fCurrentValue.cheapEqualTo(m)) {
                 continue;
             }
diff --git a/src/gpu/GrPipeline.cpp b/src/gpu/GrPipeline.cpp
index 3e5e18d..f653542 100644
--- a/src/gpu/GrPipeline.cpp
+++ b/src/gpu/GrPipeline.cpp
@@ -38,11 +38,9 @@
 
     fXferProcessor = processors.refXferProcessor();
 
-    if (args.fDstProxy.proxy()) {
-        SkASSERT(args.fDstProxy.proxy()->isInstantiated());
-
-        fDstTextureProxy = args.fDstProxy.refProxy();
-        fDstTextureOffset = args.fDstProxy.offset();
+    if (args.fDstProxyView.proxy()) {
+        fDstProxyView = args.fDstProxyView.proxyView();
+        fDstTextureOffset = args.fDstProxyView.offset();
     }
 
     // Copy GrFragmentProcessors from GrProcessorSet to Pipeline
@@ -62,19 +60,11 @@
     for (int i = 0; i < appliedClip.numClipCoverageFragmentProcessors(); ++i, ++currFPIdx) {
         fFragmentProcessors[currFPIdx] = appliedClip.detachClipCoverageFragmentProcessor(i);
     }
-
-#ifdef SK_DEBUG
-    for (int i = 0; i < numTotalProcessors; ++i) {
-        if (!fFragmentProcessors[i]->isInstantiated()) {
-            this->markAsBad();
-            break;
-        }
-    }
-#endif
 }
 
 GrXferBarrierType GrPipeline::xferBarrierType(GrTexture* texture, const GrCaps& caps) const {
-    if (fDstTextureProxy && fDstTextureProxy->peekTexture() == texture) {
+    auto proxy = fDstProxyView.proxy();
+    if (proxy && proxy->peekTexture() == texture) {
         return kTexture_GrXferBarrierType;
     }
     return this->getXferProcessor().xferBarrierType(caps);
@@ -98,7 +88,15 @@
     }
 }
 
-uint32_t GrPipeline::getBlendInfoKey() const {
+void GrPipeline::genKey(GrProcessorKeyBuilder* b, const GrCaps& caps) const {
+    // kSnapVerticesToPixelCenters is implemented in a shader.
+    InputFlags ignoredFlags = InputFlags::kSnapVerticesToPixelCenters;
+    if (!caps.multisampleDisableSupport()) {
+        // Ganesh will omit kHWAntialias regardless multisampleDisableSupport.
+        ignoredFlags |= InputFlags::kHWAntialias;
+    }
+    b->add32((uint32_t)fFlags & ~(uint32_t)ignoredFlags);
+
     const GrXferProcessor::BlendInfo& blendInfo = this->getXferProcessor().getBlendInfo();
 
     static const uint32_t kBlendWriteShift = 1;
@@ -106,10 +104,21 @@
     GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
     GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
 
-    uint32_t key = blendInfo.fWriteColor;
-    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
-    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
-    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
+    uint32_t blendKey = blendInfo.fWriteColor;
+    blendKey |= (blendInfo.fSrcBlend << kBlendWriteShift);
+    blendKey |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
+    blendKey |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
 
-    return key;
+    b->add32(blendKey);
+}
+
+void GrPipeline::visitProxies(const GrOp::VisitProxyFunc& func) const {
+    // This iteration includes any clip coverage FPs
+    for (auto [sampler, fp] : GrFragmentProcessor::PipelineTextureSamplerRange(*this)) {
+        bool mipped = (GrSamplerState::Filter::kMipMap == sampler.samplerState().filter());
+        func(sampler.proxy(), GrMipMapped(mipped));
+    }
+    if (fDstProxyView.asTextureProxy()) {
+        func(fDstProxyView.asTextureProxy(), GrMipMapped::kNo);
+    }
 }
diff --git a/src/gpu/GrPipeline.h b/src/gpu/GrPipeline.h
index b57e66d..5921062 100644
--- a/src/gpu/GrPipeline.h
+++ b/src/gpu/GrPipeline.h
@@ -14,8 +14,8 @@
 #include "src/gpu/GrFragmentProcessor.h"
 #include "src/gpu/GrNonAtomicRef.h"
 #include "src/gpu/GrProcessorSet.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrScissorState.h"
+#include "src/gpu/GrSurfaceProxyView.h"
 #include "src/gpu/GrUserStencilSettings.h"
 #include "src/gpu/GrWindowRectsState.h"
 #include "src/gpu/effects/GrCoverageSetOpXP.h"
@@ -58,7 +58,7 @@
         InputFlags fInputFlags = InputFlags::kNone;
         const GrUserStencilSettings* fUserStencil = &GrUserStencilSettings::kUnused;
         const GrCaps* fCaps = nullptr;
-        GrXferProcessor::DstProxy fDstProxy;
+        GrXferProcessor::DstProxyView fDstProxyView;
         GrSwizzle fOutputSwizzle;
     };
 
@@ -75,7 +75,7 @@
         SkIRect fScissorRect = SkIRect::EmptyIRect();
         // Must have GrPrimitiveProcessor::numTextureSamplers() entries. Can be null if no samplers
         // or textures are passed using DynamicStateArrays.
-        GrTextureProxy** fPrimitiveProcessorTextures = nullptr;
+        GrSurfaceProxy** fPrimitiveProcessorTextures = nullptr;
     };
 
     /**
@@ -87,7 +87,7 @@
         // Must have GrPrimitiveProcessor::numTextureSamplers() * num_meshes entries.
         // Can be null if no samplers or to use the same textures for all meshes via'
         // FixedDynamicState.
-        GrTextureProxy** fPrimitiveProcessorTextures = nullptr;
+        GrSurfaceProxy** fPrimitiveProcessorTextures = nullptr;
     };
 
     /**
@@ -133,19 +133,24 @@
     }
 
     /**
+     * This returns the GrSurfaceProxyView for the texture used to access the dst color. If the
+     * GrXferProcessor does not use the dst color then the proxy on the GrSurfaceProxyView will be
+     * nullptr.
+     */
+    const GrSurfaceProxyView& dstProxyView() const {
+        return fDstProxyView;
+    }
+
+    /**
      * If the GrXferProcessor uses a texture to access the dst color, then this returns that
      * texture and the offset to the dst contents within that texture.
      */
-    GrTextureProxy* dstTextureProxy(SkIPoint* offset = nullptr) const {
+    GrTexture* peekDstTexture(SkIPoint* offset = nullptr) const {
         if (offset) {
             *offset = fDstTextureOffset;
         }
 
-        return fDstTextureProxy.get();
-    }
-
-    GrTexture* peekDstTexture(SkIPoint* offset = nullptr) const {
-        if (GrTextureProxy* dstProxy = this->dstTextureProxy(offset)) {
+        if (GrTextureProxy* dstProxy = fDstProxyView.asTextureProxy()) {
             return dstProxy->peekTexture();
         }
 
@@ -186,19 +191,31 @@
     bool isStencilEnabled() const {
         return SkToBool(fFlags & Flags::kStencilEnabled);
     }
-    SkDEBUGCODE(bool isBad() const { return SkToBool(fFlags & Flags::kIsBad); })
+#ifdef SK_DEBUG
+    bool allProxiesInstantiated() const {
+        for (int i = 0; i < fFragmentProcessors.count(); ++i) {
+            if (!fFragmentProcessors[i]->isInstantiated()) {
+                return false;
+            }
+        }
+        if (fDstProxyView.proxy()) {
+            return fDstProxyView.proxy()->isInstantiated();
+        }
+
+        return true;
+    }
+#endif
 
     GrXferBarrierType xferBarrierType(GrTexture*, const GrCaps&) const;
 
     // Used by Vulkan and Metal to cache their respective pipeline objects
-    uint32_t getBlendInfoKey() const;
+    void genKey(GrProcessorKeyBuilder*, const GrCaps&) const;
 
     const GrSwizzle& outputSwizzle() const { return fOutputSwizzle; }
 
+    void visitProxies(const GrOp::VisitProxyFunc&) const;
+
 private:
-
-    SkDEBUGCODE(void markAsBad() { fFlags |= Flags::kIsBad; })
-
     static constexpr uint8_t kLastInputFlag = (uint8_t)InputFlags::kSnapVerticesToPixelCenters;
 
     /** This is a continuation of the public "InputFlags" enum. */
@@ -206,9 +223,6 @@
         kHasStencilClip = (kLastInputFlag << 1),
         kStencilEnabled = (kLastInputFlag << 2),
         kScissorEnabled = (kLastInputFlag << 3),
-#ifdef SK_DEBUG
-        kIsBad = (kLastInputFlag << 4),
-#endif
     };
 
     GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(Flags);
@@ -217,7 +231,7 @@
 
     using FragmentProcessorArray = SkAutoSTArray<8, std::unique_ptr<const GrFragmentProcessor>>;
 
-    sk_sp<GrTextureProxy> fDstTextureProxy;
+    GrSurfaceProxyView fDstProxyView;
     SkIPoint fDstTextureOffset;
     GrWindowRectsState fWindowRectsState;
     const GrUserStencilSettings* fUserStencilSettings;
diff --git a/src/gpu/GrPrimitiveProcessor.cpp b/src/gpu/GrPrimitiveProcessor.cpp
index 5ca399a..270a14c 100644
--- a/src/gpu/GrPrimitiveProcessor.cpp
+++ b/src/gpu/GrPrimitiveProcessor.cpp
@@ -8,13 +8,15 @@
 #include "src/gpu/GrPrimitiveProcessor.h"
 
 #include "src/gpu/GrCoordTransform.h"
+#include "src/gpu/GrFragmentProcessor.h"
 
 /**
  * We specialize the vertex code for each of these matrix types.
  */
 enum MatrixType {
-    kNoPersp_MatrixType  = 0,
-    kGeneral_MatrixType  = 1,
+    kNone_MatrixType     = 0,
+    kNoPersp_MatrixType  = 1,
+    kGeneral_MatrixType  = 2,
 };
 
 GrPrimitiveProcessor::GrPrimitiveProcessor(ClassID classID) : GrProcessor(classID) {}
@@ -24,19 +26,24 @@
     return this->onTextureSampler(i);
 }
 
-uint32_t
-GrPrimitiveProcessor::getTransformKey(const SkTArray<GrCoordTransform*, true>& coords,
-                                      int numCoords) const {
+uint32_t GrPrimitiveProcessor::computeCoordTransformsKey(const GrFragmentProcessor& fp) const {
+    // This is highly coupled with the code in GrGLSLGeometryProcessor::emitTransforms().
+    SkASSERT(fp.numCoordTransforms() * 2 <= 32);
     uint32_t totalKey = 0;
-    for (int t = 0; t < numCoords; ++t) {
+    for (int t = 0; t < fp.numCoordTransforms(); ++t) {
         uint32_t key = 0;
-        const GrCoordTransform* coordTransform = coords[t];
-        if (coordTransform->getMatrix().hasPerspective()) {
-            key |= kGeneral_MatrixType;
+        const GrCoordTransform& coordTransform = fp.coordTransform(t);
+        if (!fp.coordTransformsApplyToLocalCoords() && coordTransform.isNoOp()) {
+            key = kNone_MatrixType;
+        } else if (coordTransform.matrix().hasPerspective()) {
+            // Note that we can also have homogeneous varyings as a result of a GP local matrix or
+            // homogeneous local coords generated by GP. We're relying on the GP to include any
+            // variability in those in its key.
+            key = kGeneral_MatrixType;
         } else {
-            key |= kNoPersp_MatrixType;
+            key = kNoPersp_MatrixType;
         }
-        key <<= t;
+        key <<= 2*t;
         SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap
         totalKey |= key;
     }
@@ -55,20 +62,17 @@
 
 GrPrimitiveProcessor::TextureSampler::TextureSampler(const GrSamplerState& samplerState,
                                                      const GrBackendFormat& backendFormat,
-                                                     const GrSwizzle& swizzle,
-                                                     uint32_t extraSamplerKey) {
-    this->reset(samplerState, backendFormat, swizzle, extraSamplerKey);
+                                                     const GrSwizzle& swizzle) {
+    this->reset(samplerState, backendFormat, swizzle);
 }
 
 void GrPrimitiveProcessor::TextureSampler::reset(const GrSamplerState& samplerState,
                                                  const GrBackendFormat& backendFormat,
-                                                 const GrSwizzle& swizzle,
-                                                 uint32_t extraSamplerKey) {
+                                                 const GrSwizzle& swizzle) {
     fSamplerState = samplerState;
     fSamplerState.setFilterMode(clamp_filter(backendFormat.textureType(), samplerState.filter()));
     fBackendFormat = backendFormat;
     fSwizzle = swizzle;
-    fExtraSamplerKey = extraSamplerKey;
     fIsInitialized = true;
 }
 
diff --git a/src/gpu/GrPrimitiveProcessor.h b/src/gpu/GrPrimitiveProcessor.h
index e0baf65..4a0bd72 100644
--- a/src/gpu/GrPrimitiveProcessor.h
+++ b/src/gpu/GrPrimitiveProcessor.h
@@ -169,13 +169,10 @@
     virtual bool willUseGeoShader() const = 0;
 
     /**
-     * Computes a transformKey from an array of coord transforms. Will only look at the first
-     * <numCoords> transforms in the array.
-     *
-     * TODO: A better name for this function  would be "compute" instead of "get".
+     * Computes a key for the transforms owned by an FP based on the shader code that will be
+     * emitted by the primitive processor to implement them.
      */
-    uint32_t getTransformKey(const SkTArray<GrCoordTransform*, true>& coords,
-                             int numCoords) const;
+    uint32_t computeCoordTransformsKey(const GrFragmentProcessor& fp) const;
 
     /**
      * Sets a unique key on the GrProcessorKeyBuilder that is directly associated with this geometry
@@ -255,14 +252,12 @@
 public:
     TextureSampler() = default;
 
-    TextureSampler(const GrSamplerState&, const GrBackendFormat&, const GrSwizzle&,
-                   uint32_t extraSamplerKey = 0);
+    TextureSampler(const GrSamplerState&, const GrBackendFormat&, const GrSwizzle&);
 
     TextureSampler(const TextureSampler&) = delete;
     TextureSampler& operator=(const TextureSampler&) = delete;
 
-    void reset(const GrSamplerState&, const GrBackendFormat&, const GrSwizzle&,
-               uint32_t extraSamplerKey = 0);
+    void reset(const GrSamplerState&, const GrBackendFormat&, const GrSwizzle&);
 
     const GrBackendFormat& backendFormat() const { return fBackendFormat; }
     GrTextureType textureType() const { return fBackendFormat.textureType(); }
@@ -270,15 +265,12 @@
     const GrSamplerState& samplerState() const { return fSamplerState; }
     const GrSwizzle& swizzle() const { return fSwizzle; }
 
-    uint32_t extraSamplerKey() const { return fExtraSamplerKey; }
-
     bool isInitialized() const { return fIsInitialized; }
 
 private:
     GrSamplerState  fSamplerState;
     GrBackendFormat fBackendFormat;
     GrSwizzle       fSwizzle;
-    uint32_t        fExtraSamplerKey = 0;
     bool            fIsInitialized = false;
 };
 
diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp
index d7a0c57..aae1029 100644
--- a/src/gpu/GrProcessor.cpp
+++ b/src/gpu/GrProcessor.cpp
@@ -116,8 +116,8 @@
 #endif
 
     GrMemoryPool* pool() const {
-        static GrMemoryPool gPool(4096, 4096);
-        return &gPool;
+        static GrMemoryPool* gPool = new GrMemoryPool(4096, 4096);
+        return gPool;
     }
 };
 }
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index e2d75d0..60b89ab 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -105,6 +105,7 @@
         kGrDistanceFieldA8TextGeoProc_ClassID,
         kGrDistanceFieldLCDTextGeoProc_ClassID,
         kGrDistanceFieldPathGeoProc_ClassID,
+        kGrDomainEffect_ClassID,
         kGrDualIntervalGradientColorizer_ClassID,
         kGrEllipseEffect_ClassID,
         kGrFillRRectOp_Processor_ClassID,
@@ -138,7 +139,6 @@
         kGrSampleMaskProcessor_ClassID,
         kGrSaturateProcessor_ClassID,
         kGrSweepGradientLayout_ClassID,
-        kGrTextureDomainEffect_ClassID,
         kGrTextureGradientColorizer_ClassID,
         kGrTiledGradientEffect_ClassID,
         kGrTwoPointConicalGradientLayout_ClassID,
@@ -159,6 +159,7 @@
         kFwidthSquircleTestProcessor_ClassID,
         kSwizzleFragmentProcessor_ClassID,
         kTestFP_ClassID,
+        kTestRectOp_ClassID,
         kFlatNormalsFP_ClassID,
         kMappedNormalsFP_ClassID,
         kLightingFP_ClassID,
@@ -223,6 +224,6 @@
     CustomFeatures fRequestedFeatures = CustomFeatures::kNone;
 };
 
-GR_MAKE_BITFIELD_CLASS_OPS(GrProcessor::CustomFeatures);
+GR_MAKE_BITFIELD_CLASS_OPS(GrProcessor::CustomFeatures)
 
 #endif
diff --git a/src/gpu/GrProcessorSet.cpp b/src/gpu/GrProcessorSet.cpp
index cd30a88..cf75415 100644
--- a/src/gpu/GrProcessorSet.cpp
+++ b/src/gpu/GrProcessorSet.cpp
@@ -251,3 +251,10 @@
 #endif
     return analysis;
 }
+
+void GrProcessorSet::visitProxies(const GrOp::VisitProxyFunc& func) const {
+    for (auto [sampler, fp] : GrFragmentProcessor::ProcessorSetTextureSamplerRange(*this)) {
+        bool mipped = (GrSamplerState::Filter::kMipMap == sampler.samplerState().filter());
+        func(sampler.proxy(), GrMipMapped(mipped));
+    }
+}
diff --git a/src/gpu/GrProcessorSet.h b/src/gpu/GrProcessorSet.h
index 9583864..e155c67 100644
--- a/src/gpu/GrProcessorSet.h
+++ b/src/gpu/GrProcessorSet.h
@@ -153,15 +153,7 @@
     SkString dumpProcessors() const;
 #endif
 
-    void visitProxies(const GrOp::VisitProxyFunc& func) const {
-        for (int i = 0; i < this->numFragmentProcessors(); ++i) {
-            GrFragmentProcessor::TextureAccessIter iter(this->fragmentProcessor(i));
-            while (const GrFragmentProcessor::TextureSampler* sampler = iter.next()) {
-                bool mipped = (GrSamplerState::Filter::kMipMap == sampler->samplerState().filter());
-                func(sampler->proxy(), GrMipMapped(mipped));
-            }
-        }
-    }
+    void visitProxies(const GrOp::VisitProxyFunc& func) const;
 
 private:
     GrProcessorSet(Empty) : fXP((const GrXferProcessor*)nullptr), fFlags(kFinalized_Flag) {}
diff --git a/src/gpu/GrProcessorUnitTest.h b/src/gpu/GrProcessorUnitTest.h
index 6b0443e..d10f5c1 100644
--- a/src/gpu/GrProcessorUnitTest.h
+++ b/src/gpu/GrProcessorUnitTest.h
@@ -13,6 +13,7 @@
 #if GR_TEST_UTILS
 
 #include "include/private/SkTArray.h"
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrTestUtils.h"
 #include "src/gpu/GrTextureProxy.h"
 
@@ -51,17 +52,15 @@
     GrProcessorTestData(SkRandom* random,
                         GrContext* context,
                         const GrRenderTargetContext* renderTargetContext,
-                        sk_sp<GrTextureProxy> proxies[2],
-                        GrColorType proxyColorTypes[2])
-            : fRandom(random)
-            , fRenderTargetContext(renderTargetContext)
-            , fContext(context) {
+                        sk_sp<GrTextureProxy> proxies[2])
+            : fRandom(random), fRenderTargetContext(renderTargetContext), fContext(context) {
         SkASSERT(proxies[0] && proxies[1]);
         fProxies[0] = proxies[0];
         fProxies[1] = proxies[1];
-        fProxyColorTypes[0] = proxyColorTypes[0];
-        fProxyColorTypes[1] = proxyColorTypes[1];
+
+        fArena = std::unique_ptr<SkArenaAlloc>(new SkArenaAlloc(1000));
     }
+
     SkRandom* fRandom;
     const GrRenderTargetContext* fRenderTargetContext;
 
@@ -70,12 +69,13 @@
     GrProxyProvider* proxyProvider();
     const GrCaps* caps();
     sk_sp<GrTextureProxy> textureProxy(int index) { return fProxies[index]; }
-    GrColorType textureProxyColorType(int index) { return fProxyColorTypes[index]; }
+    SkArenaAlloc* allocator() { return fArena.get(); }
 
 private:
     GrContext* fContext;
     sk_sp<GrTextureProxy> fProxies[2];
-    GrColorType fProxyColorTypes[2];
+
+    std::unique_ptr<SkArenaAlloc> fArena;
 };
 
 class GrProcessor;
@@ -84,7 +84,6 @@
 template <class ProcessorSmartPtr>
 class GrProcessorTestFactory : private SkNoncopyable {
 public:
-    using Processor = typename ProcessorSmartPtr::element_type;
     using MakeProc = ProcessorSmartPtr (*)(GrProcessorTestData*);
 
     GrProcessorTestFactory(MakeProc makeProc) {
@@ -126,7 +125,7 @@
 };
 
 using GrFragmentProcessorTestFactory = GrProcessorTestFactory<std::unique_ptr<GrFragmentProcessor>>;
-using GrGeometryProcessorTestFactory = GrProcessorTestFactory<sk_sp<GrGeometryProcessor>>;
+using GrGeometryProcessorTestFactory = GrProcessorTestFactory<GrGeometryProcessor*>;
 
 class GrXPFactoryTestFactory : private SkNoncopyable {
 public:
@@ -159,7 +158,7 @@
  */
 #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST                        \
     static GrGeometryProcessorTestFactory gTestFactory SK_UNUSED; \
-    static sk_sp<GrGeometryProcessor> TestCreate(GrProcessorTestData*);
+    static GrGeometryProcessor* TestCreate(GrProcessorTestData*);
 
 #define GR_DECLARE_FRAGMENT_PROCESSOR_TEST                        \
     static GrFragmentProcessorTestFactory gTestFactory SK_UNUSED; \
@@ -193,7 +192,7 @@
 // The unit test relies on static initializers. Just declare the TestCreate function so that
 // its definitions will compile.
 #define GR_DECLARE_GEOMETRY_PROCESSOR_TEST                                                         \
-    static sk_sp<GrGeometryProcessor> TestCreate(GrProcessorTestData*);
+    static GrGeometryProcessor* TestCreate(GrProcessorTestData*);
 #define GR_DEFINE_GEOMETRY_PROCESSOR_TEST(X)
 
 // The unit test relies on static initializers. Just declare the TestGet function so that
diff --git a/src/gpu/GrProgramDesc.cpp b/src/gpu/GrProgramDesc.cpp
index a4a7548..38161c6 100644
--- a/src/gpu/GrProgramDesc.cpp
+++ b/src/gpu/GrProgramDesc.cpp
@@ -45,62 +45,48 @@
 }
 
 static uint32_t sampler_key(GrTextureType textureType, const GrSwizzle& swizzle,
-                            const GrShaderCaps& caps) {
+                            const GrCaps& caps) {
     int samplerTypeKey = texture_type_key(textureType);
 
     GR_STATIC_ASSERT(2 == sizeof(swizzle.asKey()));
     uint16_t swizzleKey = 0;
-    if (caps.textureSwizzleAppliedInShader()) {
+    if (caps.shaderCaps()->textureSwizzleAppliedInShader()) {
         swizzleKey = swizzle.asKey();
     }
     return SkToU32(samplerTypeKey | swizzleKey << kSamplerOrImageTypeKeyBits);
 }
 
 static void add_fp_sampler_keys(GrProcessorKeyBuilder* b, const GrFragmentProcessor& fp,
-                                GrGpu* gpu, const GrShaderCaps& caps) {
+                                const GrCaps& caps) {
     int numTextureSamplers = fp.numTextureSamplers();
     if (!numTextureSamplers) {
         return;
     }
     for (int i = 0; i < numTextureSamplers; ++i) {
         const GrFragmentProcessor::TextureSampler& sampler = fp.textureSampler(i);
-        const GrTexture* tex = sampler.peekTexture();
-        uint32_t samplerKey = sampler_key(
-                tex->texturePriv().textureType(), sampler.swizzle(), caps);
-        uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
-                sampler.samplerState(), sampler.proxy()->backendFormat());
-        if (extraSamplerKey) {
-            // We first mark the normal sampler key with last bit to flag that it has an extra
-            // sampler key. We then add both keys.
-            SkASSERT((samplerKey & (1 << 31)) == 0);
-            b->add32(samplerKey | (1 << 31));
-            b->add32(extraSamplerKey);
-        } else {
-            b->add32(samplerKey);
-        }
+        const GrBackendFormat& backendFormat = sampler.proxy()->backendFormat();
+
+        uint32_t samplerKey = sampler_key(backendFormat.textureType(), sampler.swizzle(), caps);
+        b->add32(samplerKey);
+
+        caps.addExtraSamplerKey(b, sampler.samplerState(), backendFormat);
     }
 }
 
 static void add_pp_sampler_keys(GrProcessorKeyBuilder* b, const GrPrimitiveProcessor& pp,
-                                const GrShaderCaps& caps) {
+                                const GrCaps& caps) {
     int numTextureSamplers = pp.numTextureSamplers();
     if (!numTextureSamplers) {
         return;
     }
     for (int i = 0; i < numTextureSamplers; ++i) {
         const GrPrimitiveProcessor::TextureSampler& sampler = pp.textureSampler(i);
-        uint32_t samplerKey = sampler_key(
-                sampler.textureType(), sampler.swizzle(), caps);
-        uint32_t extraSamplerKey = sampler.extraSamplerKey();
-        if (extraSamplerKey) {
-            // We first mark the normal sampler key with last bit to flag that it has an extra
-            // sampler key. We then add both keys.
-            SkASSERT((samplerKey & (1 << 31)) == 0);
-            b->add32(samplerKey | (1 << 31));
-            b->add32(extraSamplerKey);
-        } else {
-            b->add32(samplerKey);
-        }
+        const GrBackendFormat& backendFormat = sampler.backendFormat();
+
+        uint32_t samplerKey = sampler_key(backendFormat.textureType(), sampler.swizzle(), caps);
+        b->add32(samplerKey);
+
+        caps.addExtraSamplerKey(b, sampler.samplerState(), backendFormat);
     }
 }
 
@@ -114,8 +100,7 @@
  * function because it is hairy, though FPs do not have attribs, and GPs do not have transforms
  */
 static bool gen_fp_meta_key(const GrFragmentProcessor& fp,
-                            GrGpu* gpu,
-                            const GrShaderCaps& shaderCaps,
+                            const GrCaps& caps,
                             uint32_t transformKey,
                             GrProcessorKeyBuilder* b) {
     size_t processorKeySize = b->size();
@@ -127,7 +112,7 @@
         return false;
     }
 
-    add_fp_sampler_keys(b, fp, gpu, shaderCaps);
+    add_fp_sampler_keys(b, fp, caps);
 
     uint32_t* key = b->add32n(2);
     key[0] = (classID << 16) | SkToU32(processorKeySize);
@@ -136,7 +121,7 @@
 }
 
 static bool gen_pp_meta_key(const GrPrimitiveProcessor& pp,
-                            const GrShaderCaps& shaderCaps,
+                            const GrCaps& caps,
                             uint32_t transformKey,
                             GrProcessorKeyBuilder* b) {
     size_t processorKeySize = b->size();
@@ -148,7 +133,7 @@
         return false;
     }
 
-    add_pp_sampler_keys(b, pp, shaderCaps);
+    add_pp_sampler_keys(b, pp, caps);
 
     uint32_t* key = b->add32n(2);
     key[0] = (classID << 16) | SkToU32(processorKeySize);
@@ -156,9 +141,7 @@
     return true;
 }
 
-static bool gen_xp_meta_key(const GrXferProcessor& xp,
-                            const GrShaderCaps& shaderCaps,
-                            GrProcessorKeyBuilder* b) {
+static bool gen_xp_meta_key(const GrXferProcessor& xp, GrProcessorKeyBuilder* b) {
     size_t processorKeySize = b->size();
     uint32_t classID = xp.classID();
 
@@ -174,32 +157,33 @@
 
 static bool gen_frag_proc_and_meta_keys(const GrPrimitiveProcessor& primProc,
                                         const GrFragmentProcessor& fp,
-                                        GrGpu* gpu,
-                                        const GrShaderCaps& shaderCaps,
+                                        const GrCaps& caps,
                                         GrProcessorKeyBuilder* b) {
     for (int i = 0; i < fp.numChildProcessors(); ++i) {
-        if (!gen_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), gpu, shaderCaps, b)) {
+        if (!gen_frag_proc_and_meta_keys(primProc, fp.childProcessor(i), caps, b)) {
             return false;
         }
     }
 
-    fp.getGLSLProcessorKey(shaderCaps, b);
+    fp.getGLSLProcessorKey(*caps.shaderCaps(), b);
 
-    return gen_fp_meta_key(fp, gpu, shaderCaps, primProc.getTransformKey(fp.coordTransforms(),
-                                                                         fp.numCoordTransforms()),
-                                                                         b);
+    return gen_fp_meta_key(fp, caps, primProc.computeCoordTransformsKey(fp), b);
 }
 
 bool GrProgramDesc::Build(GrProgramDesc* desc, const GrRenderTarget* renderTarget,
-                          const GrProgramInfo& programInfo, GrPrimitiveType primitiveType,
-                          GrGpu* gpu) {
+                          const GrProgramInfo& programInfo, const GrCaps& caps) {
+
+#ifdef SK_DEBUG
+    if (renderTarget) {
+        SkASSERT(programInfo.backendFormat() == renderTarget->backendFormat());
+    }
+#endif
+
     // The descriptor is used as a cache key. Thus when a field of the
     // descriptor will not affect program generation (because of the attribute
     // bindings in use or other descriptor field settings) it should be set
     // to a canonical value to avoid duplicate programs with different keys.
 
-    const GrShaderCaps& shaderCaps = *gpu->caps()->shaderCaps();
-
     GR_STATIC_ASSERT(0 == kProcessorKeysOffset % sizeof(uint32_t));
     // Make room for everything up to the effect keys.
     desc->key().reset();
@@ -207,16 +191,16 @@
 
     GrProcessorKeyBuilder b(&desc->key());
 
-    programInfo.primProc().getGLSLProcessorKey(shaderCaps, &b);
+    programInfo.primProc().getGLSLProcessorKey(*caps.shaderCaps(), &b);
     programInfo.primProc().getAttributeKey(&b);
-    if (!gen_pp_meta_key(programInfo.primProc(), shaderCaps, 0, &b)) {
+    if (!gen_pp_meta_key(programInfo.primProc(), caps, 0, &b)) {
         desc->key().reset();
         return false;
     }
 
     for (int i = 0; i < programInfo.pipeline().numFragmentProcessors(); ++i) {
         const GrFragmentProcessor& fp = programInfo.pipeline().getFragmentProcessor(i);
-        if (!gen_frag_proc_and_meta_keys(programInfo.primProc(), fp, gpu, shaderCaps, &b)) {
+        if (!gen_frag_proc_and_meta_keys(programInfo.primProc(), fp, caps, &b)) {
             desc->key().reset();
             return false;
         }
@@ -225,12 +209,12 @@
     const GrXferProcessor& xp = programInfo.pipeline().getXferProcessor();
     const GrSurfaceOrigin* originIfDstTexture = nullptr;
     GrSurfaceOrigin origin;
-    if (programInfo.pipeline().dstTextureProxy()) {
-        origin = programInfo.pipeline().dstTextureProxy()->origin();
+    if (programInfo.pipeline().dstProxyView().proxy()) {
+        origin = programInfo.pipeline().dstProxyView().origin();
         originIfDstTexture = &origin;
     }
-    xp.getGLSLProcessorKey(shaderCaps, &b, originIfDstTexture);
-    if (!gen_xp_meta_key(xp, shaderCaps, &b)) {
+    xp.getGLSLProcessorKey(*caps.shaderCaps(), &b, originIfDstTexture);
+    if (!gen_xp_meta_key(xp, &b)) {
         desc->key().reset();
         return false;
     }
@@ -254,6 +238,7 @@
     if (header->fColorFragmentProcessorCnt != programInfo.pipeline().numColorFragmentProcessors() ||
         header->fCoverageFragmentProcessorCnt !=
                                          programInfo.pipeline().numCoverageFragmentProcessors()) {
+        desc->key().reset();
         return false;
     }
     // If we knew the shader won't depend on origin, we could skip this (and use the same program
@@ -264,6 +249,16 @@
     // Ensure enough bits.
     SkASSERT(header->fProcessorFeatures == (int) programInfo.requestedFeatures());
     header->fSnapVerticesToPixelCenters = programInfo.pipeline().snapVerticesToPixelCenters();
-    header->fHasPointSize = (primitiveType == GrPrimitiveType::kPoints);
+    // The base descriptor only stores whether or not the primitiveType is kPoints. Backend-
+    // specific versions (e.g., Vulkan) require more detail
+    header->fHasPointSize = (programInfo.primitiveType() == GrPrimitiveType::kPoints);
+
+    header->fInitialKeyLength = desc->keyLength();
+    // Fail if the initial key length won't fit in 27 bits.
+    if (header->fInitialKeyLength != desc->keyLength()) {
+        desc->key().reset();
+        return false;
+    }
+
     return true;
 }
diff --git a/src/gpu/GrProgramDesc.h b/src/gpu/GrProgramDesc.h
index 391bfe1..c0cfaa8 100644
--- a/src/gpu/GrProgramDesc.h
+++ b/src/gpu/GrProgramDesc.h
@@ -11,42 +11,18 @@
 #include "include/private/GrTypesPriv.h"
 #include "include/private/SkTArray.h"
 #include "include/private/SkTo.h"
-#include "src/core/SkOpts.h"
-#include "src/gpu/GrColor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 
+class GrCaps;
 class GrProgramInfo;
+class GrRenderTarget;
 class GrShaderCaps;
 
-/** This class describes a program to generate. It also serves as a program cache key */
+/** This class is used to generate a generic program cache key. The Dawn, Metal and Vulkan
+ *  backends derive backend-specific versions which add additional information.
+ */
 class GrProgramDesc {
 public:
-    // Creates an uninitialized key that must be populated by GrGpu::buildProgramDesc()
-    GrProgramDesc() {}
-
-    /**
-     * Builds a program descriptor. Before the descriptor can be used, the client must call finalize
-     * on the filled in GrProgramDesc.
-     *
-     * @param desc          The built and finalized descriptor
-     * @param renderTarget  The target of the draw
-     * @param programInfo   Program information need to build the key
-     * @param primitiveType Controls whether the shader will output a point size.
-     * @param gpu           Pointer to the GrGpu object the program will be used with.
-     **/
-    static bool Build(GrProgramDesc*, const GrRenderTarget*, const GrProgramInfo&,
-                      GrPrimitiveType, GrGpu*);
-
-    // This is strictly an OpenGL call since the other backends have additional data in their
-    // keys
-    static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) {
-        if (!SkTFitsIn<int>(keyLength)) {
-            return false;
-        }
-        desc->fKey.reset(SkToInt(keyLength));
-        memcpy(desc->fKey.begin(), keyData, keyLength);
-        return true;
-    }
+    bool isValid() const { return !fKey.empty(); }
 
     // Returns this as a uint32_t array to be used as a key in the program cache.
     const uint32_t* asKey() const {
@@ -87,23 +63,57 @@
         return !(*this == other);
     }
 
-    // TODO: remove this use of the header
-    bool hasPointSize() const { return this->header().fHasPointSize; }
+    uint32_t initialKeyLength() const { return this->header().fInitialKeyLength; }
 
 protected:
+    friend class GrDawnCaps;
+    friend class GrGLCaps;
+    friend class GrMockCaps;
+    friend class GrMtlCaps;
+    friend class GrVkCaps;
+
+    friend class GrGLGpu; // for ProgramCache to access BuildFromData
+
+    // Creates an uninitialized key that must be populated by Build
+    GrProgramDesc() {}
+
+    /**
+     * Builds a program descriptor.
+     *
+     * @param desc          The built descriptor
+     * @param renderTarget  The target of the draw
+     * @param programInfo   Program information need to build the key
+     * @param caps          the caps
+     **/
+    static bool Build(GrProgramDesc*, const GrRenderTarget*, const GrProgramInfo&, const GrCaps&);
+
+    // This is strictly an OpenGL call since the other backends have additional data in their
+    // keys
+    static bool BuildFromData(GrProgramDesc* desc, const void* keyData, size_t keyLength) {
+        if (!SkTFitsIn<int>(keyLength)) {
+            return false;
+        }
+        desc->fKey.reset(SkToInt(keyLength));
+        memcpy(desc->fKey.begin(), keyData, keyLength);
+        return true;
+    }
+
+    // TODO: this should be removed and converted to just data added to the key
     struct KeyHeader {
         // Set to uniquely identify any swizzling of the shader's output color(s).
         uint16_t fOutputSwizzle;
         uint8_t fColorFragmentProcessorCnt; // Can be packed into 4 bits if required.
         uint8_t fCoverageFragmentProcessorCnt;
         // Set to uniquely identify the rt's origin, or 0 if the shader does not require this info.
-        uint8_t fSurfaceOriginKey : 2;
-        uint8_t fProcessorFeatures : 1;
-        bool fSnapVerticesToPixelCenters : 1;
-        bool fHasPointSize : 1;
-        uint8_t fPad : 3;
+        uint32_t fSurfaceOriginKey : 2;
+        uint32_t fProcessorFeatures : 1;
+        uint32_t fSnapVerticesToPixelCenters : 1;
+        uint32_t fHasPointSize : 1;
+        // This is the key size (in bytes) after core key construction. It doesn't include any
+        // portions added by the platform-specific backends.
+        uint32_t fInitialKeyLength : 27;
     };
-    GR_STATIC_ASSERT(sizeof(KeyHeader) == 6);
+    GR_STATIC_ASSERT(sizeof(KeyHeader) == 8);
 
     const KeyHeader& header() const { return *this->atOffset<KeyHeader, kHeaderOffset>(); }
 
@@ -121,7 +131,6 @@
     enum KeyOffsets {
         kHeaderOffset = 0,
         kHeaderSize = SkAlign4(sizeof(KeyHeader)),
-        // Part 4.
         // This is the offset into the backenend specific part of the key, which includes
         // per-processor keys.
         kProcessorKeysOffset = kHeaderOffset + kHeaderSize,
@@ -135,7 +144,6 @@
     };
 
     SkSTArray<kPreAllocSize, uint8_t, true>& key() { return fKey; }
-    const SkSTArray<kPreAllocSize, uint8_t, true>& key() const { return fKey; }
 
 private:
     SkSTArray<kPreAllocSize, uint8_t, true> fKey;
diff --git a/src/gpu/GrProgramInfo.cpp b/src/gpu/GrProgramInfo.cpp
index 3391b61..2b0fb84 100644
--- a/src/gpu/GrProgramInfo.cpp
+++ b/src/gpu/GrProgramInfo.cpp
@@ -7,24 +7,39 @@
 
 #include "src/gpu/GrProgramInfo.h"
 
+#include "src/gpu/GrStencilSettings.h"
+
+GrStencilSettings GrProgramInfo::nonGLStencilSettings() const {
+    GrStencilSettings stencil;
+
+    if (this->pipeline().isStencilEnabled()) {
+        stencil.reset(*this->pipeline().getUserStencil(),
+                      this->pipeline().hasStencilClip(),
+                      8);
+    }
+
+    return stencil;
+}
 
 #ifdef SK_DEBUG
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrTexturePriv.h"
 
-void GrProgramInfo::validate() const {
-    SkASSERT(!fPipeline.isBad());
+void GrProgramInfo::validate(bool flushTime) const {
+    if (flushTime) {
+        SkASSERT(fPipeline->allProxiesInstantiated());
+    }
 
     if (this->hasDynamicPrimProcTextures()) {
         SkASSERT(!this->hasFixedPrimProcTextures());
-        SkASSERT(fPrimProc.numTextureSamplers());
+        SkASSERT(fPrimProc->numTextureSamplers());
     } else if (this->hasFixedPrimProcTextures()) {
-        SkASSERT(fPrimProc.numTextureSamplers());
+        SkASSERT(fPrimProc->numTextureSamplers());
     } else {
-        SkASSERT(!fPrimProc.numTextureSamplers());
+        SkASSERT(!fPrimProc->numTextureSamplers());
     }
 
-    SkASSERT(!fPipeline.isScissorEnabled() || this->hasFixedScissor() ||
+    SkASSERT(!fPipeline->isScissorEnabled() || this->hasFixedScissor() ||
              this->hasDynamicScissors());
 
     if (this->hasDynamicPrimProcTextures()) {
@@ -34,15 +49,16 @@
             auto dynamicPrimProcTextures = this->dynamicPrimProcTextures(0);
 
             const GrBackendFormat& format = dynamicPrimProcTextures[s]->backendFormat();
-            GrTextureType type = dynamicPrimProcTextures[s]->textureType();
+            GrTextureType type = dynamicPrimProcTextures[s]->backendFormat().textureType();
             GrPixelConfig config = dynamicPrimProcTextures[s]->config();
 
             for (int m = 1; m < fNumDynamicStateArrays; ++m) {
                 dynamicPrimProcTextures = this->dynamicPrimProcTextures(m);
 
                 auto testProxy = dynamicPrimProcTextures[s];
+                SkASSERT(testProxy->asTextureProxy());
                 SkASSERT(testProxy->backendFormat() == format);
-                SkASSERT(testProxy->textureType() == type);
+                SkASSERT(testProxy->backendFormat().textureType() == type);
                 SkASSERT(testProxy->config() == config);
             }
         }
@@ -100,12 +116,8 @@
         }
     }
 
-    GrFragmentProcessor::Iter iter(this->pipeline());
-    while (const GrFragmentProcessor* fp = iter.next()) {
-        for (int s = 0; s < fp->numTextureSamplers(); ++s) {
-            const auto& textureSampler = fp->textureSampler(s);
-            assertResolved(textureSampler.peekTexture(), textureSampler.samplerState());
-        }
+    for (auto [sampler, fp] : GrFragmentProcessor::PipelineTextureSamplerRange(this->pipeline())) {
+        assertResolved(sampler.peekTexture(), sampler.samplerState());
     }
 }
 
@@ -113,8 +125,8 @@
     SkASSERT(!fNumDynamicStateArrays || meshCount == fNumDynamicStateArrays);
 
     for (int i = 0; i < meshCount; ++i) {
-        SkASSERT(fPrimProc.hasVertexAttributes() == meshes[i].hasVertexData());
-        SkASSERT(fPrimProc.hasInstanceAttributes() == meshes[i].hasInstanceData());
+        SkASSERT(fPrimProc->hasVertexAttributes() == meshes[i].hasVertexData());
+        SkASSERT(fPrimProc->hasInstanceAttributes() == meshes[i].hasInstanceData());
     }
 }
 
diff --git a/src/gpu/GrProgramInfo.h b/src/gpu/GrProgramInfo.h
index a4c46e6..2cad573 100644
--- a/src/gpu/GrProgramInfo.h
+++ b/src/gpu/GrProgramInfo.h
@@ -13,43 +13,54 @@
 #include "src/gpu/GrPrimitiveProcessor.h"
 
 class GrMesh;
+class GrStencilSettings;
 
 class GrProgramInfo {
 public:
     GrProgramInfo(int numSamples,
+                  int numStencilSamples,
+                  const GrBackendFormat& backendFormat,
                   GrSurfaceOrigin origin,
-                  const GrPipeline& pipeline,
-                  const GrPrimitiveProcessor& primProc,
+                  const GrPipeline* pipeline,
+                  const GrPrimitiveProcessor* primProc,
                   const GrPipeline::FixedDynamicState* fixedDynamicState,
                   const GrPipeline::DynamicStateArrays* dynamicStateArrays,
-                  int numDynamicStateArrays)
-            : fNumSamples(numSamples)
+                  int numDynamicStateArrays,
+                  GrPrimitiveType primitiveType)
+            : fNumRasterSamples(pipeline->isStencilEnabled() ? numStencilSamples : numSamples)
+            , fIsMixedSampled(fNumRasterSamples > numSamples)
+            , fBackendFormat(backendFormat)
             , fOrigin(origin)
             , fPipeline(pipeline)
             , fPrimProc(primProc)
             , fFixedDynamicState(fixedDynamicState)
             , fDynamicStateArrays(dynamicStateArrays)
-            , fNumDynamicStateArrays(numDynamicStateArrays) {
-        fRequestedFeatures = fPrimProc.requestedFeatures();
-        for (int i = 0; i < fPipeline.numFragmentProcessors(); ++i) {
-            fRequestedFeatures |= fPipeline.getFragmentProcessor(i).requestedFeatures();
+            , fNumDynamicStateArrays(numDynamicStateArrays)
+            , fPrimitiveType(primitiveType) {
+        SkASSERT(fNumRasterSamples > 0);
+        fRequestedFeatures = fPrimProc->requestedFeatures();
+        for (int i = 0; i < fPipeline->numFragmentProcessors(); ++i) {
+            fRequestedFeatures |= fPipeline->getFragmentProcessor(i).requestedFeatures();
         }
-        fRequestedFeatures |= fPipeline.getXferProcessor().requestedFeatures();
+        fRequestedFeatures |= fPipeline->getXferProcessor().requestedFeatures();
 
-        SkDEBUGCODE(this->validate();)
+        SkDEBUGCODE(this->validate(false);)
         (void) fNumDynamicStateArrays;  // touch this to quiet unused member warnings
     }
 
     GrProcessor::CustomFeatures requestedFeatures() const { return fRequestedFeatures; }
 
-    int numSamples() const { return fNumSamples;  }
+    int numRasterSamples() const { return fNumRasterSamples;  }
+    bool isMixedSampled() const { return fIsMixedSampled; }
+    // The backend format of the destination render target [proxy]
+    const GrBackendFormat& backendFormat() const { return fBackendFormat; }
     GrSurfaceOrigin origin() const { return fOrigin;  }
-    const GrPipeline& pipeline() const { return fPipeline; }
-    const GrPrimitiveProcessor& primProc() const { return fPrimProc; }
+    const GrPipeline& pipeline() const { return *fPipeline; }
+    const GrPrimitiveProcessor& primProc() const { return *fPrimProc; }
     const GrPipeline::FixedDynamicState* fixedDynamicState() const { return fFixedDynamicState; }
 
     bool hasDynamicScissors() const {
-        return fPipeline.isScissorEnabled() &&
+        return fPipeline->isScissorEnabled() &&
                fDynamicStateArrays && fDynamicStateArrays->fScissorRects;
     }
 
@@ -59,7 +70,7 @@
         return fDynamicStateArrays->fScissorRects[i];
     }
 
-    bool hasFixedScissor() const { return fPipeline.isScissorEnabled() && fFixedDynamicState; }
+    bool hasFixedScissor() const { return fPipeline->isScissorEnabled() && fFixedDynamicState; }
 
     const SkIRect& fixedScissor() const {
         SkASSERT(this->hasFixedScissor());
@@ -71,45 +82,58 @@
         return fDynamicStateArrays && fDynamicStateArrays->fPrimitiveProcessorTextures;
     }
 
-    const GrTextureProxy* const* dynamicPrimProcTextures(int i) const {
+    const GrSurfaceProxy* const* dynamicPrimProcTextures(int i) const {
         SkASSERT(this->hasDynamicPrimProcTextures());
         SkASSERT(i < fNumDynamicStateArrays);
 
         return fDynamicStateArrays->fPrimitiveProcessorTextures +
-                                                                i * fPrimProc.numTextureSamplers();
+                                                                i * fPrimProc->numTextureSamplers();
     }
 
     bool hasFixedPrimProcTextures() const {
         return fFixedDynamicState && fFixedDynamicState->fPrimitiveProcessorTextures;
     }
 
-    const GrTextureProxy* const* fixedPrimProcTextures() const {
+    const GrSurfaceProxy* const* fixedPrimProcTextures() const {
         SkASSERT(this->hasFixedPrimProcTextures());
 
         return fFixedDynamicState->fPrimitiveProcessorTextures;
     }
 
+    GrPrimitiveType primitiveType() const { return fPrimitiveType; }
+
+    // For Dawn, Metal and Vulkan the number of stencil bits is known a priori so we can
+    // create the stencil settings here.
+    GrStencilSettings nonGLStencilSettings() const;
+
+    void visitProxies(const GrOp::VisitProxyFunc& fn) const {
+        fPipeline->visitProxies(fn);
+    }
+
 #ifdef SK_DEBUG
-    void validate() const;
+    void validate(bool flushTime) const;
     void checkAllInstantiated() const;
     void checkMSAAAndMIPSAreResolved() const;
     void compatibleWithMeshes(const GrMesh meshes[], int meshCount) const;
 
     bool isNVPR() const {
-        return fPrimProc.isPathRendering() && !fPrimProc.willUseGeoShader() &&
-               !fPrimProc.numVertexAttributes() && !fPrimProc.numInstanceAttributes();
+        return fPrimProc->isPathRendering() && !fPrimProc->willUseGeoShader() &&
+               !fPrimProc->numVertexAttributes() && !fPrimProc->numInstanceAttributes();
     }
 #endif
 
 private:
-    const int                             fNumSamples;
+    const int                             fNumRasterSamples;
+    const bool                            fIsMixedSampled;
+    const GrBackendFormat                 fBackendFormat;
     const GrSurfaceOrigin                 fOrigin;
-    const GrPipeline&                     fPipeline;
-    const GrPrimitiveProcessor&           fPrimProc;
+    const GrPipeline*                     fPipeline;
+    const GrPrimitiveProcessor*           fPrimProc;
     const GrPipeline::FixedDynamicState*  fFixedDynamicState;
     const GrPipeline::DynamicStateArrays* fDynamicStateArrays;
     const int                             fNumDynamicStateArrays;
     GrProcessor::CustomFeatures           fRequestedFeatures;
+    GrPrimitiveType                       fPrimitiveType;
 };
 
 #endif
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index 33dd09d..ba2f7e9 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -198,9 +198,8 @@
     GrSwizzle texSwizzle = this->caps()->getTextureSwizzle(tex->backendFormat(), colorType);
 
     if (tex->asRenderTarget()) {
-        GrSwizzle outSwizzle = this->caps()->getOutputSwizzle(tex->backendFormat(), colorType);
         return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(
-                std::move(tex), origin, texSwizzle, outSwizzle, useAllocator));
+                std::move(tex), origin, texSwizzle, useAllocator));
     } else {
         return sk_sp<GrTextureProxy>(
                 new GrTextureProxy(std::move(tex), origin, texSwizzle, useAllocator));
@@ -473,10 +472,9 @@
         SkASSERT(renderTargetSampleCnt);
         // We know anything we instantiate later from this deferred path will be
         // both texturable and renderable
-        GrSwizzle outSwizzle = caps->getOutputSwizzle(format, colorType);
         return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(
                 *caps, format, copyDesc, renderTargetSampleCnt, origin, mipMapped, mipMapsStatus,
-                texSwizzle, outSwizzle, fit, budgeted, isProtected, surfaceFlags, useAllocator));
+                texSwizzle, fit, budgeted, isProtected, surfaceFlags, useAllocator));
     }
 
     return sk_sp<GrTextureProxy>(new GrTextureProxy(format, copyDesc, origin, mipMapped,
@@ -611,10 +609,9 @@
     SkASSERT(GrBudgetedType::kBudgeted != tex->resourcePriv().budgetedType());
 
     GrSwizzle texSwizzle = caps->getTextureSwizzle(tex->backendFormat(), colorType);
-    GrSwizzle outSwizzle = caps->getOutputSwizzle(tex->backendFormat(), colorType);
 
     return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(std::move(tex), origin, texSwizzle,
-                                                                outSwizzle, UseAllocator::kNo));
+                                                                UseAllocator::kNo));
 }
 
 sk_sp<GrSurfaceProxy> GrProxyProvider::wrapBackendRenderTarget(
@@ -649,10 +646,9 @@
     SkASSERT(GrBudgetedType::kBudgeted != rt->resourcePriv().budgetedType());
 
     GrSwizzle texSwizzle = caps->getTextureSwizzle(rt->backendFormat(), grColorType);
-    GrSwizzle outSwizzle = caps->getOutputSwizzle(rt->backendFormat(), grColorType);
 
     return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(std::move(rt), origin, texSwizzle,
-                                                              outSwizzle, UseAllocator::kNo));
+                                                              UseAllocator::kNo));
 }
 
 sk_sp<GrSurfaceProxy> GrProxyProvider::wrapBackendTextureAsRenderTarget(
@@ -683,10 +679,9 @@
     SkASSERT(GrBudgetedType::kBudgeted != rt->resourcePriv().budgetedType());
 
     GrSwizzle texSwizzle = caps->getTextureSwizzle(rt->backendFormat(), grColorType);
-    GrSwizzle outSwizzle = caps->getOutputSwizzle(rt->backendFormat(), grColorType);
 
     return sk_sp<GrSurfaceProxy>(new GrRenderTargetProxy(std::move(rt), origin, texSwizzle,
-                                                         outSwizzle, UseAllocator::kNo));
+                                                         UseAllocator::kNo));
 }
 
 sk_sp<GrRenderTargetProxy> GrProxyProvider::wrapVulkanSecondaryCBAsRenderTarget(
@@ -716,7 +711,6 @@
 
     GrColorType colorType = SkColorTypeToGrColorType(imageInfo.colorType());
     GrSwizzle texSwizzle = this->caps()->getTextureSwizzle(rt->backendFormat(), colorType);
-    GrSwizzle outSwizzle = this->caps()->getOutputSwizzle(rt->backendFormat(), colorType);
 
     if (!this->caps()->isFormatAsColorTypeRenderable(colorType, rt->backendFormat(),
                                                      rt->numSamples())) {
@@ -725,7 +719,7 @@
 
     // All Vulkan surfaces uses top left origins.
     return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(
-            std::move(rt), kTopLeft_GrSurfaceOrigin, texSwizzle, outSwizzle, UseAllocator::kNo,
+            std::move(rt), kTopLeft_GrSurfaceOrigin, texSwizzle, UseAllocator::kNo,
             GrRenderTargetProxy::WrapsVkSecondaryCB::kYes));
 }
 
@@ -756,7 +750,6 @@
 
     GrColorType colorType = GrPixelConfigToColorType(desc.fConfig);
     GrSwizzle texSwizzle = this->caps()->getTextureSwizzle(format, colorType);
-    GrSwizzle outSwizzle = this->caps()->getOutputSwizzle(format, colorType);
 
     if (renderable == GrRenderable::kYes) {
         return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(*this->caps(),
@@ -768,7 +761,6 @@
                                                                     mipMapped,
                                                                     mipMapsStatus,
                                                                     texSwizzle,
-                                                                    outSwizzle,
                                                                     fit,
                                                                     budgeted,
                                                                     isProtected,
@@ -814,7 +806,6 @@
 
     GrColorType colorType = GrPixelConfigToColorType(desc.fConfig);
     GrSwizzle texSwizzle = this->caps()->getTextureSwizzle(format, colorType);
-    GrSwizzle outSwizzle = this->caps()->getOutputSwizzle(format, colorType);
 
     if (textureInfo) {
         // Wrapped vulkan secondary command buffers don't support texturing since we won't have an
@@ -822,8 +813,8 @@
         SkASSERT(!wrapsVkSecondaryCB);
         return sk_sp<GrRenderTargetProxy>(new GrTextureRenderTargetProxy(
                 *this->caps(), std::move(callback), format, desc, sampleCnt, origin,
-                textureInfo->fMipMapped, mipMapsStatus, texSwizzle, outSwizzle, fit, budgeted,
-                isProtected, surfaceFlags, useAllocator));
+                textureInfo->fMipMapped, mipMapsStatus, texSwizzle, fit, budgeted, isProtected,
+                surfaceFlags, useAllocator));
     }
 
     GrRenderTargetProxy::WrapsVkSecondaryCB vkSCB =
@@ -831,8 +822,8 @@
                                : GrRenderTargetProxy::WrapsVkSecondaryCB::kNo;
 
     return sk_sp<GrRenderTargetProxy>(new GrRenderTargetProxy(
-            std::move(callback), format, desc, sampleCnt, origin, texSwizzle, outSwizzle, fit,
-            budgeted, isProtected, surfaceFlags, useAllocator, vkSCB));
+            std::move(callback), format, desc, sampleCnt, origin, texSwizzle, fit, budgeted,
+            isProtected, surfaceFlags, useAllocator, vkSCB));
 }
 
 sk_sp<GrTextureProxy> GrProxyProvider::MakeFullyLazyProxy(LazyInstantiateCallback&& callback,
@@ -857,13 +848,12 @@
 
     GrColorType colorType = GrPixelConfigToColorType(desc.fConfig);
     GrSwizzle texSwizzle = caps.getTextureSwizzle(format, colorType);
-    GrSwizzle outSwizzle = caps.getOutputSwizzle(format, colorType);
 
     if (GrRenderable::kYes == renderable) {
         return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(
                 caps, std::move(callback), format, desc, renderTargetSampleCnt, origin,
-                GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated, texSwizzle, outSwizzle,
-                SkBackingFit::kApprox, SkBudgeted::kYes, isProtected, surfaceFlags, useAllocator));
+                GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated, texSwizzle, SkBackingFit::kApprox,
+                SkBudgeted::kYes, isProtected, surfaceFlags, useAllocator));
     } else {
         return sk_sp<GrTextureProxy>(new GrTextureProxy(
                 std::move(callback), format, desc, origin, GrMipMapped::kNo,
diff --git a/src/gpu/GrRecordingContext.cpp b/src/gpu/GrRecordingContext.cpp
index 58eb8c5..b7f9d46 100644
--- a/src/gpu/GrRecordingContext.cpp
+++ b/src/gpu/GrRecordingContext.cpp
@@ -137,18 +137,20 @@
 
 // Stored in this arena:
 //     GrTextureOp's DynamicStateArrays and FixedDynamicState
-SkArenaAlloc* GrRecordingContext::opPODAllocator() {
-    if (!fOpPODAllocator) {
+//     some GrGeometryProcessors, GrPipelines and GrProgramInfos
+SkArenaAlloc* GrRecordingContext::recordTimeAllocator() {
+    if (!fRecordTimeAllocator) {
         // TODO: empirically determine a better number for SkArenaAlloc's firstHeapAllocation param
-        fOpPODAllocator = std::unique_ptr<SkArenaAlloc>(new SkArenaAlloc(sizeof(GrPipeline) * 100));
+        fRecordTimeAllocator = std::unique_ptr<SkArenaAlloc>(
+                                                    new SkArenaAlloc(sizeof(GrPipeline) * 100));
     }
 
-    SkASSERT(fOpPODAllocator);
-    return fOpPODAllocator.get();
+    SkASSERT(fRecordTimeAllocator);
+    return fRecordTimeAllocator.get();
 }
 
-std::unique_ptr<SkArenaAlloc> GrRecordingContext::detachOpPOD() {
-    return std::move(fOpPODAllocator);
+std::unique_ptr<SkArenaAlloc> GrRecordingContext::detachRecordTimeAllocator() {
+    return std::move(fRecordTimeAllocator);
 }
 
 GrTextBlobCache* GrRecordingContext::getTextBlobCache() {
@@ -322,8 +324,8 @@
     return fContext->refCaps();
 }
 
-std::unique_ptr<SkArenaAlloc> GrRecordingContextPriv::detachOpPOD() {
-    return fContext->detachOpPOD();
+std::unique_ptr<SkArenaAlloc> GrRecordingContextPriv::detachRecordTimeAllocator() {
+    return fContext->detachRecordTimeAllocator();
 }
 
 sk_sp<GrSkSLFPFactoryCache> GrRecordingContextPriv::fpFactoryCache() {
diff --git a/src/gpu/GrRecordingContextPriv.h b/src/gpu/GrRecordingContextPriv.h
index dfaf914..c11873d 100644
--- a/src/gpu/GrRecordingContextPriv.h
+++ b/src/gpu/GrRecordingContextPriv.h
@@ -46,8 +46,8 @@
     sk_sp<GrOpMemoryPool> refOpMemoryPool();
     GrOpMemoryPool* opMemoryPool() { return fContext->opMemoryPool(); }
 
-    SkArenaAlloc* opPODAllocator() { return fContext->opPODAllocator(); }
-    std::unique_ptr<SkArenaAlloc> detachOpPOD();
+    SkArenaAlloc* recordTimeAllocator() { return fContext->recordTimeAllocator(); }
+    std::unique_ptr<SkArenaAlloc> detachRecordTimeAllocator();
 
     GrStrikeCache* getGrStrikeCache() { return fContext->getGrStrikeCache(); }
     GrTextBlobCache* getTextBlobCache() { return fContext->getTextBlobCache(); }
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 11da2ea..8c9dde7 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -21,6 +21,7 @@
 #include "src/core/SkMatrixPriv.h"
 #include "src/core/SkRRectPriv.h"
 #include "src/core/SkSurfacePriv.h"
+#include "src/core/SkYUVMath.h"
 #include "src/gpu/GrAppliedClip.h"
 #include "src/gpu/GrAuditTrail.h"
 #include "src/gpu/GrBlurUtils.h"
@@ -217,7 +218,7 @@
 
     if (!fOpsTask || fOpsTask->isClosed()) {
         sk_sp<GrOpsTask> newOpsTask =
-                this->drawingManager()->newOpsTask(fRenderTargetProxy, fManagedOpsTask);
+                this->drawingManager()->newOpsTask(this->outputSurfaceView(), fManagedOpsTask);
         if (fOpsTask && fNumStencilSamples > 0) {
             // Store the stencil values in memory upon completion of fOpsTask.
             fOpsTask->setMustPreserveStencil();
@@ -609,8 +610,8 @@
 }
 
 void GrRenderTargetContext::drawTexturedQuad(const GrClip& clip,
-                                             sk_sp<GrTextureProxy> proxy,
-                                             GrColorType srcColorType,
+                                             GrSurfaceProxyView proxyView,
+                                             SkAlphaType srcAlphaType,
                                              sk_sp<GrColorSpaceXform> textureXform,
                                              GrSamplerState::Filter filter,
                                              const SkPMColor4f& color,
@@ -623,7 +624,7 @@
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
-    SkASSERT(proxy);
+    SkASSERT(proxyView.asTextureProxy());
     GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTexturedQuad", fContext);
 
     AutoCheckFlush acf(this->drawingManager());
@@ -646,11 +647,11 @@
                                                           : GrTextureOp::Saturate::kNo;
         // Use the provided domain, although hypothetically we could detect that the cropped local
         // quad is sufficiently inside the domain and the constraint could be dropped.
-        this->addDrawOp(finalClip,
-                        GrTextureOp::Make(fContext, std::move(proxy), srcColorType,
-                                          std::move(textureXform), filter, color, saturate,
-                                          blendMode, aaType, edgeFlags, croppedDeviceQuad,
-                                          croppedLocalQuad, domain));
+        this->addDrawOp(
+                finalClip,
+                GrTextureOp::Make(fContext, std::move(proxyView), srcAlphaType,
+                                  std::move(textureXform), filter, color, saturate, blendMode,
+                                  aaType, edgeFlags, croppedDeviceQuad, croppedLocalQuad, domain));
     }
 }
 
@@ -732,8 +733,9 @@
                                         const SkMatrix& viewMatrix, const QuadSetEntry quads[],
                                         int cnt) {
     GrAAType aaType = this->chooseAAType(aa);
-    this->addDrawOp(clip, GrFillRectOp::MakeSet(fContext, std::move(paint), aaType, viewMatrix,
-                                                quads, cnt));
+
+    GrFillRectOp::AddFillRectOps(this, clip, fContext, std::move(paint), aaType, viewMatrix,
+                                 quads, cnt);
 }
 
 int GrRenderTargetContextPriv::maxWindowRectangles() const {
@@ -871,47 +873,15 @@
     SkDEBUGCODE(this->validate();)
     GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
 
-    if (mode != SkBlendMode::kSrcOver ||
-        !fContext->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
-        // Draw one at a time since the bulk API doesn't support non src-over blending, or the
-        // backend can't support the bulk geometry processor yet.
-        SkMatrix ctm;
-        for (int i = 0; i < cnt; ++i) {
-            float alpha = set[i].fAlpha;
-            ctm = viewMatrix;
-            if (set[i].fPreViewMatrix) {
-                ctm.preConcat(*set[i].fPreViewMatrix);
-            }
-
-            GrQuad quad, srcQuad;
-            if (set[i].fDstClipQuad) {
-                quad = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm);
-
-                SkPoint srcPts[4];
-                GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4);
-                srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
-            } else {
-                quad = GrQuad::MakeFromRect(set[i].fDstRect, ctm);
-                srcQuad = GrQuad(set[i].fSrcRect);
-            }
-
-            const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint
-                    ? &set[i].fSrcRect : nullptr;
-            this->drawTexturedQuad(clip, set[i].fProxy, set[i].fSrcColorType, texXform, filter,
-                                   {alpha, alpha, alpha, alpha}, mode, aa, set[i].fAAFlags,
-                                   quad, srcQuad, domain);
-        }
-    } else {
-        // Can use a single op, avoiding GrPaint creation, and can batch across proxies
-        AutoCheckFlush acf(this->drawingManager());
-        GrAAType aaType = this->chooseAAType(aa);
-        auto clampType = GrColorTypeClampType(this->colorInfo().colorType());
-        auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
-                                                          : GrTextureOp::Saturate::kNo;
-        auto op = GrTextureOp::MakeSet(fContext, set, cnt, filter, saturate, aaType, constraint,
-                                       viewMatrix, std::move(texXform));
-        this->addDrawOp(clip, std::move(op));
-    }
+    // Create the minimum number of GrTextureOps needed to draw this set. Individual
+    // GrTextureOps can rebind the texture between draws thus avoiding GrPaint (re)creation.
+    AutoCheckFlush acf(this->drawingManager());
+    GrAAType aaType = this->chooseAAType(aa);
+    auto clampType = GrColorTypeClampType(this->colorInfo().colorType());
+    auto saturate = clampType == GrClampType::kManual ? GrTextureOp::Saturate::kYes
+                                                      : GrTextureOp::Saturate::kNo;
+    GrTextureOp::AddTextureSetOps(this, clip, fContext, set, cnt, filter, saturate, mode, aaType,
+                                  constraint, viewMatrix, std::move(texXform));
 }
 
 void GrRenderTargetContext::drawVertices(const GrClip& clip,
@@ -1345,7 +1315,7 @@
     path.setIsVolatile(true);
     path.addRRect(inner);
     path.addRRect(outer);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, GrShape(path));
 }
 
@@ -1445,7 +1415,7 @@
     assert_alive(paint);
     this->drawShapeUsingPathRenderer(
             clip, std::move(paint), aa, viewMatrix,
-            GrShape(SkRRect::MakeOval(oval), SkPath::kCW_Direction, 2, false, style));
+            GrShape(SkRRect::MakeOval(oval), SkPathDirection::kCW, 2, false, style));
 }
 
 void GrRenderTargetContext::drawArc(const GrClip& clip,
@@ -1529,9 +1499,7 @@
         return;
     }
     auto dstCT = SkColorTypeToGrColorType(info.colorType());
-    // TODO: Support reading to gray.
-    if (dstCT == GrColorType::kUnknown ||
-        GrColorTypeComponentFlags(dstCT) & kGray_SkColorTypeComponentFlag) {
+    if (dstCT == GrColorType::kUnknown) {
         callback(context, nullptr);
         return;
     }
@@ -1590,7 +1558,6 @@
             // If the src is not texturable first try to make a copy to a texture.
             if (!texProxy) {
                 texProxy = GrSurfaceProxy::Copy(fContext, fRenderTargetProxy.get(),
-                                                this->colorInfo().colorType(),
                                                 GrMipMapped::kNo, srcRect, SkBackingFit::kApprox,
                                                 SkBudgeted::kNo);
                 if (!texProxy) {
@@ -1608,8 +1575,8 @@
                 return;
             }
             tempRTC->drawTexture(GrNoClip(), std::move(texProxy), this->colorInfo().colorType(),
-                                 GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
-                                 SK_PMColor4fWHITE, srcRectToDraw,
+                                 this->colorInfo().alphaType(), GrSamplerState::Filter::kNearest,
+                                 SkBlendMode::kSrc, SK_PMColor4fWHITE, srcRectToDraw,
                                  SkRect::MakeWH(srcRect.width(), srcRect.height()), GrAA::kNo,
                                  GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
                                  SkMatrix::I(), std::move(xform));
@@ -1693,6 +1660,11 @@
     SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
     SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
 
+    if (this->asSurfaceProxy()->isProtected()) {
+        callback(context, nullptr);
+        return;
+    }
+
     auto directContext = fContext->priv().asDirectContext();
     SkASSERT(directContext);
     auto mappedBufferManager = directContext->priv().clientMappedBufferManager();
@@ -1771,6 +1743,10 @@
         callback(context, nullptr);
         return;
     }
+    if (this->asSurfaceProxy()->isProtected()) {
+        callback(context, nullptr);
+        return;
+    }
     int x = srcRect.fLeft;
     int y = srcRect.fTop;
     std::unique_ptr<GrRenderTargetContext> tempRTC;
@@ -1810,8 +1786,8 @@
                 return;
             }
             tempRTC->drawTexture(GrNoClip(), std::move(texProxy), this->colorInfo().colorType(),
-                                 GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
-                                 SK_PMColor4fWHITE, srcRectToDraw,
+                                 this->colorInfo().alphaType(), GrSamplerState::Filter::kNearest,
+                                 SkBlendMode::kSrc, SK_PMColor4fWHITE, srcRectToDraw,
                                  SkRect::MakeWH(srcRect.width(), srcRect.height()), GrAA::kNo,
                                  GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
                                  SkMatrix::I(), std::move(xform));
@@ -1824,8 +1800,6 @@
         callback(context, nullptr);
         return;
     }
-    GrColorType srcColorType = tempRTC ? tempRTC->colorInfo().colorType()
-                                       : this->colorInfo().colorType();
 
     auto yRTC = direct->priv().makeDeferredRenderTargetContextWithFallback(
             SkBackingFit::kApprox, dstSize.width(), dstSize.height(), GrColorType::kAlpha_8,
@@ -1843,41 +1817,9 @@
         return;
     }
 
-    static constexpr float kRec601M[] {
-             65.481f / 255, 128.553f / 255,  24.966f / 255,  16.f / 255,   // y
-            -37.797f / 255, -74.203f / 255, 112.0f   / 255, 128.f / 255,  // u
-            112.f    / 255, -93.786f / 255, -18.214f / 255, 128.f / 255,  // v
-    };
-    static constexpr float kRec709M[] {
-             45.5594f / 255,  156.6288f / 255,  15.8118f / 255,  16.f / 255, // y
-            -25.6642f / 255,  -86.3358f / 255, 112.f     / 255, 128.f / 255,  // u
-            112.f     / 255, -101.7303f / 255, -10.2697f / 255, 128.f / 255,  // v
-    };
-    static constexpr float kJpegM[] {
-             0.299f   ,  0.587f   ,  0.114f   ,   0.f / 255,  // y
-            -0.168736f, -0.331264f,  0.5f     , 128.f / 255,  // u
-             0.5f     , -0.418688f, -0.081312f, 128.f / 255,  // v
-    };
-    static constexpr float kIM[] {
-            1.f, 0.f, 0.f, 0.f,
-            0.f, 1.f, 0.f, 0.f,
-            0.f, 0.f, 1.f, 0.f,
-    };
-    const float* baseM = kIM;
-    switch (yuvColorSpace) {
-        case kRec601_SkYUVColorSpace:
-            baseM = kRec601M;
-            break;
-        case kRec709_SkYUVColorSpace:
-            baseM = kRec709M;
-            break;
-        case kJPEG_SkYUVColorSpace:
-            baseM = kJpegM;
-            break;
-        case kIdentity_SkYUVColorSpace:
-            baseM = kIM;
-            break;
-    }
+    float baseM[20];
+    SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
+
     // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
 
     auto texMatrix = SkMatrix::MakeTrans(x, y);
@@ -1888,9 +1830,9 @@
     // This matrix generates (r,g,b,a) = (0, 0, 0, y)
     float yM[20];
     std::fill_n(yM, 15, 0.f);
-    yM[15] = baseM[0]; yM[16] = baseM[1]; yM[17] = baseM[2]; yM[18] = 0; yM[19] = baseM[3];
+    std::copy_n(baseM + 0, 5, yM + 15);
     GrPaint yPaint;
-    yPaint.addColorTextureProcessor(srcProxy, srcColorType, texMatrix);
+    yPaint.addColorTextureProcessor(srcProxy, this->colorInfo().alphaType(), texMatrix);
     auto yFP = GrColorMatrixFragmentProcessor::Make(yM, false, true, false);
     yPaint.addColorFragmentProcessor(std::move(yFP));
     yPaint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -1907,9 +1849,9 @@
     // This matrix generates (r,g,b,a) = (0, 0, 0, u)
     float uM[20];
     std::fill_n(uM, 15, 0.f);
-    uM[15] = baseM[4]; uM[16] = baseM[5]; uM[17] = baseM[6]; uM[18] = 0; uM[19] = baseM[7];
+    std::copy_n(baseM + 5, 5, uM + 15);
     GrPaint uPaint;
-    uPaint.addColorTextureProcessor(srcProxy, srcColorType, texMatrix,
+    uPaint.addColorTextureProcessor(srcProxy, this->colorInfo().alphaType(), texMatrix,
                                     GrSamplerState::ClampBilerp());
     auto uFP = GrColorMatrixFragmentProcessor::Make(uM, false, true, false);
     uPaint.addColorFragmentProcessor(std::move(uFP));
@@ -1926,9 +1868,9 @@
     // This matrix generates (r,g,b,a) = (0, 0, 0, v)
     float vM[20];
     std::fill_n(vM, 15, 0.f);
-    vM[15] = baseM[8]; vM[16] = baseM[9]; vM[17] = baseM[10]; vM[18] = 0; vM[19] = baseM[11];
+    std::copy_n(baseM + 10, 5, vM + 15);
     GrPaint vPaint;
-    vPaint.addColorTextureProcessor(srcProxy, srcColorType, texMatrix,
+    vPaint.addColorTextureProcessor(srcProxy, this->colorInfo().alphaType(), texMatrix,
                                     GrSamplerState::ClampBilerp());
     auto vFP = GrColorMatrixFragmentProcessor::Make(vM, false, true, false);
     vPaint.addColorFragmentProcessor(std::move(vFP));
@@ -2024,7 +1966,8 @@
 
     auto resourceProvider = direct->priv().resourceProvider();
 
-    std::unique_ptr<sk_sp<GrSemaphore>[]> grSemaphores(new sk_sp<GrSemaphore>[numSemaphores]);
+    std::unique_ptr<std::unique_ptr<GrSemaphore>[]> grSemaphores(
+            new std::unique_ptr<GrSemaphore>[numSemaphores]);
     for (int i = 0; i < numSemaphores; ++i) {
         grSemaphores[i] = resourceProvider->wrapBackendSemaphore(
                 waitSemaphores[i], GrResourceProvider::SemaphoreWrapType::kWillWait,
@@ -2327,35 +2270,34 @@
     GrAppliedClip appliedClip;
     GrDrawOp::FixedFunctionFlags fixedFunctionFlags = op->fixedFunctionFlags();
     bool usesHWAA = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesHWAA;
-    bool usesStencil = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil;
+    bool usesUserStencilBits = fixedFunctionFlags & GrDrawOp::FixedFunctionFlags::kUsesStencil;
 
-    if (usesStencil) {
+    if (usesUserStencilBits) {  // Stencil clipping will call setNeedsStencil on its own, if needed.
         this->setNeedsStencil(usesHWAA);
     }
 
-    if (!clip.apply(fContext, this, usesHWAA, usesStencil, &appliedClip, &bounds)) {
+    if (!clip.apply(fContext, this, usesHWAA, usesUserStencilBits, &appliedClip, &bounds)) {
         fContext->priv().opMemoryPool()->release(std::move(op));
         return;
     }
 
-    SkASSERT((!usesStencil && !appliedClip.hasStencilClip()) || (fNumStencilSamples > 0));
+    bool willUseStencil = usesUserStencilBits || appliedClip.hasStencilClip();
+    SkASSERT(!willUseStencil || fNumStencilSamples > 0);
+
+    // If stencil is enabled and the framebuffer is mixed sampled, then the graphics pipeline will
+    // have mixed sampled coverage, regardless of whether HWAA is enabled. (e.g., a non-aa draw
+    // that uses a stencil test when the stencil buffer is multisampled.)
+    bool hasMixedSampledCoverage = (
+            willUseStencil && fNumStencilSamples > this->numSamples());
+    SkASSERT(!hasMixedSampledCoverage || fRenderTargetProxy->canUseMixedSamples(*this->caps()));
 
     GrClampType clampType = GrColorTypeClampType(this->colorInfo().colorType());
-    // MIXED SAMPLES TODO: If we start using mixed samples for clips we will need to check the clip
-    // here as well.
-    bool hasMixedSampledCoverage = (usesHWAA && this->numSamples() <= 1);
-#ifdef SK_DEBUG
-    if (hasMixedSampledCoverage) {
-        SkASSERT(usesStencil);
-        SkASSERT(fRenderTargetProxy->canUseMixedSamples(*this->caps()));
-    }
-#endif
     GrProcessorSet::Analysis analysis = op->finalize(
             *this->caps(), &appliedClip, hasMixedSampledCoverage, clampType);
 
-    GrXferProcessor::DstProxy dstProxy;
+    GrXferProcessor::DstProxyView dstProxyView;
     if (analysis.requiresDstTexture()) {
-        if (!this->setupDstProxy(clip, *op, &dstProxy)) {
+        if (!this->setupDstProxyView(clip, *op, &dstProxyView)) {
             fContext->priv().opMemoryPool()->release(std::move(op));
             return;
         }
@@ -2366,12 +2308,12 @@
     if (willAddFn) {
         willAddFn(op.get(), opsTask->uniqueID());
     }
-    opsTask->addDrawOp(std::move(op), analysis, std::move(appliedClip), dstProxy,
+    opsTask->addDrawOp(std::move(op), analysis, std::move(appliedClip), dstProxyView,
                        GrTextureResolveManager(this->drawingManager()), *this->caps());
 }
 
-bool GrRenderTargetContext::setupDstProxy(const GrClip& clip, const GrOp& op,
-                                          GrXferProcessor::DstProxy* dstProxy) {
+bool GrRenderTargetContext::setupDstProxyView(const GrClip& clip, const GrOp& op,
+                                              GrXferProcessor::DstProxyView* dstProxyView) {
     // If we are wrapping a vulkan secondary command buffer, we can't make a dst copy because we
     // don't actually have a VkImage to make a copy of. Additionally we don't have the power to
     // start and stop the render pass in order to make the copy.
@@ -2380,11 +2322,11 @@
     }
 
     if (this->caps()->textureBarrierSupport() && !fRenderTargetProxy->requiresManualMSAAResolve()) {
-        if (GrTextureProxy* texProxy = fRenderTargetProxy->asTextureProxy()) {
+        if (fRenderTargetProxy->asTextureProxy()) {
             // The render target is a texture, so we can read from it directly in the shader. The XP
             // will be responsible to detect this situation and request a texture barrier.
-            dstProxy->setProxy(sk_ref_sp(texProxy));
-            dstProxy->setOffset(0, 0);
+            dstProxyView->setProxyView(this->textureSurfaceView());
+            dstProxyView->setOffset(0, 0);
             return true;
         }
     }
@@ -2432,18 +2374,18 @@
         dstOffset = {copyRect.fLeft, copyRect.fTop};
         fit = SkBackingFit::kApprox;
     }
-    sk_sp<GrTextureProxy> newProxy = GrSurfaceProxy::Copy(
-            fContext, fRenderTargetProxy.get(), this->colorInfo().colorType(), GrMipMapped::kNo,
-            copyRect, fit, SkBudgeted::kYes, restrictions.fRectsMustMatch);
+    sk_sp<GrTextureProxy> newProxy =
+            GrSurfaceProxy::Copy(fContext, fRenderTargetProxy.get(), GrMipMapped::kNo, copyRect,
+                                 fit, SkBudgeted::kYes, restrictions.fRectsMustMatch);
     SkASSERT(newProxy);
 
-    dstProxy->setProxy(std::move(newProxy));
-    dstProxy->setOffset(dstOffset);
+    dstProxyView->setProxyView({std::move(newProxy), this->origin(), this->textureSwizzle()});
+    dstProxyView->setOffset(dstOffset);
     return true;
 }
 
-bool GrRenderTargetContext::blitTexture(GrTextureProxy* src, GrColorType srcColorType,
-                                        const SkIRect& srcRect, const SkIPoint& dstPoint) {
+bool GrRenderTargetContext::blitTexture(GrTextureProxy* src, const SkIRect& srcRect,
+                                        const SkIPoint& dstPoint) {
     SkIRect clippedSrcRect;
     SkIPoint clippedDstPoint;
     if (!GrClipSrcRectAndDstPoint(this->asSurfaceProxy()->dimensions(), src->dimensions(), srcRect,
@@ -2453,7 +2395,7 @@
 
     GrPaint paint;
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    auto fp = GrSimpleTextureEffect::Make(sk_ref_sp(src), srcColorType, SkMatrix::I());
+    auto fp = GrSimpleTextureEffect::Make(sk_ref_sp(src), kUnknown_SkAlphaType, SkMatrix::I());
     if (!fp) {
         return false;
     }
@@ -2466,4 +2408,3 @@
             SkRect::Make(clippedSrcRect));
     return true;
 }
-
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 286bff5..ef34f40 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -196,15 +196,18 @@
      * device space.
      */
     void drawTexture(const GrClip& clip, sk_sp<GrTextureProxy> proxy, GrColorType srcColorType,
-                     GrSamplerState::Filter filter, SkBlendMode mode, const SkPMColor4f& color,
-                     const SkRect& srcRect, const SkRect& dstRect, GrAA aa, GrQuadAAFlags edgeAA,
-                     SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
-                     sk_sp<GrColorSpaceXform> texXform) {
+                     SkAlphaType srcAlphaType, GrSamplerState::Filter filter, SkBlendMode mode,
+                     const SkPMColor4f& color, const SkRect& srcRect, const SkRect& dstRect,
+                     GrAA aa, GrQuadAAFlags edgeAA, SkCanvas::SrcRectConstraint constraint,
+                     const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform) {
         const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint ?
                 &srcRect : nullptr;
-        this->drawTexturedQuad(clip, std::move(proxy), srcColorType, std::move(texXform), filter,
-                               color, mode, aa, edgeAA, GrQuad::MakeFromRect(dstRect, viewMatrix),
-                               GrQuad(srcRect), domain);
+        GrSurfaceOrigin origin = proxy->origin();
+        const GrSwizzle& swizzle = proxy->textureSwizzle();
+        GrSurfaceProxyView proxyView(std::move(proxy), origin, swizzle);
+        this->drawTexturedQuad(clip, std::move(proxyView), srcAlphaType, std::move(texXform),
+                               filter, color, mode, aa, edgeAA,
+                               GrQuad::MakeFromRect(dstRect, viewMatrix), GrQuad(srcRect), domain);
     }
 
     /**
@@ -214,19 +217,24 @@
      * provided, the strict src rect constraint is applied using 'domain'.
      */
     void drawTextureQuad(const GrClip& clip, sk_sp<GrTextureProxy> proxy, GrColorType srcColorType,
-                         GrSamplerState::Filter filter, SkBlendMode mode, const SkPMColor4f& color,
-                         const SkPoint srcQuad[4], const SkPoint dstQuad[4], GrAA aa,
-                         GrQuadAAFlags edgeAA, const SkRect* domain, const SkMatrix& viewMatrix,
+                         SkAlphaType srcAlphaType, GrSamplerState::Filter filter, SkBlendMode mode,
+                         const SkPMColor4f& color, const SkPoint srcQuad[4],
+                         const SkPoint dstQuad[4], GrAA aa, GrQuadAAFlags edgeAA,
+                         const SkRect* domain, const SkMatrix& viewMatrix,
                          sk_sp<GrColorSpaceXform> texXform) {
-        this->drawTexturedQuad(clip, std::move(proxy), srcColorType, std::move(texXform), filter,
-                               color, mode, aa, edgeAA, GrQuad::MakeFromSkQuad(dstQuad, viewMatrix),
+        GrSurfaceOrigin origin = proxy->origin();
+        const GrSwizzle& swizzle = proxy->textureSwizzle();
+        GrSurfaceProxyView proxyView(std::move(proxy), origin, swizzle);
+        this->drawTexturedQuad(clip, std::move(proxyView), srcAlphaType, std::move(texXform),
+                               filter, color, mode, aa, edgeAA,
+                               GrQuad::MakeFromSkQuad(dstQuad, viewMatrix),
                                GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()), domain);
     }
 
     /** Used with drawTextureSet */
     struct TextureSetEntry {
-        sk_sp<GrTextureProxy> fProxy;
-        GrColorType fSrcColorType;
+        GrSurfaceProxyView fProxyView;
+        SkAlphaType fSrcAlphaType;
         SkRect fSrcRect;
         SkRect fDstRect;
         const SkPoint* fDstClipQuad; // Must be null, or point to an array of 4 points
@@ -435,8 +443,7 @@
      * of the srcRect. The srcRect and dstRect are clipped to the bounds of the src and dst surfaces
      * respectively.
      */
-    bool blitTexture(GrTextureProxy* src, GrColorType srcColorType, const SkIRect& srcRect,
-                     const SkIPoint& dstPoint);
+    bool blitTexture(GrTextureProxy* src, const SkIRect& srcRect, const SkIPoint& dstPoint);
 
     /**
      * Adds the necessary signal and wait semaphores and adds the passed in SkDrawable to the
@@ -542,12 +549,8 @@
     friend class GrTessellatingPathRenderer;         // for access to add[Mesh]DrawOp
     friend class GrCCPerFlushResources;              // for access to addDrawOp
     friend class GrCoverageCountingPathRenderer;     // for access to addDrawOp
-    // for a unit test
-    friend void test_draw_op(GrContext*,
-                             GrRenderTargetContext*,
-                             std::unique_ptr<GrFragmentProcessor>,
-                             sk_sp<GrTextureProxy>,
-                             GrColorType);
+    friend class GrFillRectOp;                       // for access to addDrawOp
+    friend class GrTextureOp;                        // for access to addDrawOp
 
     GrRenderTargetContext(GrRecordingContext*, sk_sp<GrRenderTargetProxy>, GrColorType,
                           GrSurfaceOrigin, GrSwizzle texSwizzle, GrSwizzle outSwizzle,
@@ -601,8 +604,8 @@
 
     // Like drawFilledQuad but does not require using a GrPaint or FP for texturing
     void drawTexturedQuad(const GrClip& clip,
-                          sk_sp<GrTextureProxy> proxy,
-                          GrColorType srcColorType,
+                          GrSurfaceProxyView proxyView,
+                          SkAlphaType alphaType,
                           sk_sp<GrColorSpaceXform> textureXform,
                           GrSamplerState::Filter filter,
                           const SkPMColor4f& color,
@@ -630,8 +633,8 @@
     // Makes a copy of the proxy if it is necessary for the draw and places the texture that should
     // be used by GrXferProcessor to access the destination color in 'result'. If the return
     // value is false then a texture copy could not be made.
-    bool SK_WARN_UNUSED_RESULT setupDstProxy(const GrClip&, const GrOp& op,
-                                             GrXferProcessor::DstProxy* result);
+    bool SK_WARN_UNUSED_RESULT setupDstProxyView(const GrClip&, const GrOp& op,
+                                                 GrXferProcessor::DstProxyView* result);
 
     class AsyncReadResult;
 
diff --git a/src/gpu/GrRenderTargetProxy.cpp b/src/gpu/GrRenderTargetProxy.cpp
index c3c92e8..8268269 100644
--- a/src/gpu/GrRenderTargetProxy.cpp
+++ b/src/gpu/GrRenderTargetProxy.cpp
@@ -27,7 +27,6 @@
                                          int sampleCount,
                                          GrSurfaceOrigin origin,
                                          const GrSwizzle& textureSwizzle,
-                                         const GrSwizzle& outputSwizzle,
                                          SkBackingFit fit,
                                          SkBudgeted budgeted,
                                          GrProtected isProtected,
@@ -36,8 +35,7 @@
         : INHERITED(format, desc, GrRenderable::kYes, origin, textureSwizzle, fit, budgeted,
                     isProtected, surfaceFlags, useAllocator)
         , fSampleCnt(sampleCount)
-        , fWrapsVkSecondaryCB(WrapsVkSecondaryCB::kNo)
-        , fOutputSwizzle(outputSwizzle) {}
+        , fWrapsVkSecondaryCB(WrapsVkSecondaryCB::kNo) {}
 
 // Lazy-callback version
 GrRenderTargetProxy::GrRenderTargetProxy(LazyInstantiateCallback&& callback,
@@ -46,7 +44,6 @@
                                          int sampleCount,
                                          GrSurfaceOrigin origin,
                                          const GrSwizzle& textureSwizzle,
-                                         const GrSwizzle& outputSwizzle,
                                          SkBackingFit fit,
                                          SkBudgeted budgeted,
                                          GrProtected isProtected,
@@ -56,20 +53,17 @@
         : INHERITED(std::move(callback), format, desc, GrRenderable::kYes, origin, textureSwizzle,
                     fit, budgeted, isProtected, surfaceFlags, useAllocator)
         , fSampleCnt(sampleCount)
-        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB)
-        , fOutputSwizzle(outputSwizzle) {}
+        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB) {}
 
 // Wrapped version
 GrRenderTargetProxy::GrRenderTargetProxy(sk_sp<GrSurface> surf,
                                          GrSurfaceOrigin origin,
                                          const GrSwizzle& textureSwizzle,
-                                         const GrSwizzle& outputSwizzle,
                                          UseAllocator useAllocator,
                                          WrapsVkSecondaryCB wrapsVkSecondaryCB)
         : INHERITED(std::move(surf), origin, textureSwizzle, SkBackingFit::kExact, useAllocator)
         , fSampleCnt(fTarget->asRenderTarget()->numSamples())
-        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB)
-        , fOutputSwizzle(outputSwizzle) {
+        , fWrapsVkSecondaryCB(wrapsVkSecondaryCB) {
     // The kRequiresManualMSAAResolve flag better not be set if we are not multisampled or if
     // MSAA resolve should happen automatically.
     //
@@ -89,8 +83,8 @@
     if (this->isLazy()) {
         return false;
     }
-    if (!this->instantiateImpl(resourceProvider, fSampleCnt, fNumStencilSamples, GrRenderable::kYes,
-                               GrMipMapped::kNo, nullptr)) {
+    if (!this->instantiateImpl(resourceProvider, fSampleCnt, GrRenderable::kYes, GrMipMapped::kNo,
+                               nullptr)) {
         return false;
     }
 
@@ -109,8 +103,8 @@
 }
 
 sk_sp<GrSurface> GrRenderTargetProxy::createSurface(GrResourceProvider* resourceProvider) const {
-    sk_sp<GrSurface> surface = this->createSurfaceImpl(
-            resourceProvider, fSampleCnt, fNumStencilSamples, GrRenderable::kYes, GrMipMapped::kNo);
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, fSampleCnt,
+                                                       GrRenderable::kYes, GrMipMapped::kNo);
     if (!surface) {
         return nullptr;
     }
diff --git a/src/gpu/GrRenderTargetProxy.h b/src/gpu/GrRenderTargetProxy.h
index 944fe81..b67554c 100644
--- a/src/gpu/GrRenderTargetProxy.h
+++ b/src/gpu/GrRenderTargetProxy.h
@@ -45,10 +45,7 @@
     }
 
     /**
-     * Returns the number of stencil samples required by this proxy.
-     * NOTE: Once instantiated, the actual render target may have more samples, but it is guaranteed
-     * to have at least this many. (After a multisample stencil buffer has been attached to a render
-     * target, we never "downgrade" it to one with fewer samples.)
+     * Returns the number of stencil samples this proxy will use, or 0 if it does not use stencil.
      */
     int numStencilSamples() const { return fNumStencilSamples; }
 
@@ -59,8 +56,6 @@
 
     int maxWindowRectangles(const GrCaps& caps) const;
 
-    const GrSwizzle& outputSwizzle() const { return fOutputSwizzle; }
-
     bool wrapsVkSecondaryCB() const { return fWrapsVkSecondaryCB == WrapsVkSecondaryCB::kYes; }
 
     void markMSAADirty(const SkIRect& dirtyRect) {
@@ -99,7 +94,6 @@
                         int sampleCount,
                         GrSurfaceOrigin,
                         const GrSwizzle& textureSwizzle,
-                        const GrSwizzle& outputSwizzle,
                         SkBackingFit,
                         SkBudgeted,
                         GrProtected,
@@ -124,7 +118,6 @@
                         int sampleCount,
                         GrSurfaceOrigin,
                         const GrSwizzle& textureSwizzle,
-                        const GrSwizzle& outputSwizzle,
                         SkBackingFit,
                         SkBudgeted,
                         GrProtected,
@@ -136,7 +129,6 @@
     GrRenderTargetProxy(sk_sp<GrSurface>,
                         GrSurfaceOrigin,
                         const GrSwizzle& textureSwizzle,
-                        const GrSwizzle& outputSwizzle,
                         UseAllocator,
                         WrapsVkSecondaryCB = WrapsVkSecondaryCB::kNo);
 
@@ -164,14 +156,14 @@
     int8_t             fSampleCnt;
     int8_t             fNumStencilSamples = 0;
     WrapsVkSecondaryCB fWrapsVkSecondaryCB;
-    GrSwizzle          fOutputSwizzle;
     SkIRect            fMSAADirtyRect = SkIRect::MakeEmpty();
-    // This is to fix issue in large comment above. Without the padding we end 6 bytes into a 16
-    // byte range, so the GrTextureProxy ends up starting 8 byte aligned by not 16. We add the
-    // padding here to get us right up to the 16 byte alignment (technically any padding of 3-10
-    // bytes would work since it always goes up to 8 byte alignment, but we use 10 to more explicit
-    // about what we're doing).
-    char               fDummyPadding[10];
+    // This is to fix issue in large comment above. Without the padding we can end up with the
+    // GrTextureProxy starting 8 byte aligned by not 16. This happens when the RT ends at bytes 1-8.
+    // Note: with the virtual inheritance an 8 byte pointer is at the start of GrRenderTargetProxy.
+    //
+    // In the current world we end the RT proxy at 12 bytes. Technically any padding between 0-4
+    // will work, but we use 4 to be more explicit about getting it to 16 byte alignment.
+    char               fDummyPadding[4];
 
     typedef GrSurfaceProxy INHERITED;
 };
diff --git a/src/gpu/GrRenderTask.cpp b/src/gpu/GrRenderTask.cpp
index 2f6c0e6..b1a5a8b 100644
--- a/src/gpu/GrRenderTask.cpp
+++ b/src/gpu/GrRenderTask.cpp
@@ -21,16 +21,22 @@
     return id;
 }
 
-GrRenderTask::GrRenderTask(sk_sp<GrSurfaceProxy> target)
-        : fTarget(std::move(target))
+GrRenderTask::GrRenderTask()
+        : fUniqueID(CreateUniqueID())
+        , fFlags(0) {
+}
+
+GrRenderTask::GrRenderTask(GrSurfaceProxyView targetView)
+        : fTargetView(std::move(targetView))
         , fUniqueID(CreateUniqueID())
         , fFlags(0) {
 }
 
 GrRenderTask::~GrRenderTask() {
-    if (fTarget && this == fTarget->getLastRenderTask()) {
+    GrSurfaceProxy* proxy = fTargetView.proxy();
+    if (proxy && this == proxy->getLastRenderTask()) {
         // Ensure the target proxy doesn't keep hold of a dangling back pointer.
-        fTarget->setLastRenderTask(nullptr);
+        proxy->setLastRenderTask(nullptr);
     }
 }
 
@@ -53,12 +59,13 @@
 
     SkIRect targetUpdateBounds;
     if (ExpectedOutcome::kTargetDirty == this->onMakeClosed(caps, &targetUpdateBounds)) {
-        SkASSERT(SkIRect::MakeSize(fTarget->dimensions()).contains(targetUpdateBounds));
-        if (fTarget->requiresManualMSAAResolve()) {
-            SkASSERT(fTarget->asRenderTargetProxy());
-            fTarget->asRenderTargetProxy()->markMSAADirty(targetUpdateBounds);
+        GrSurfaceProxy* proxy = fTargetView.proxy();
+        SkASSERT(SkIRect::MakeSize(proxy->dimensions()).contains(targetUpdateBounds));
+        if (proxy->requiresManualMSAAResolve()) {
+            SkASSERT(fTargetView.asRenderTargetProxy());
+            fTargetView.asRenderTargetProxy()->markMSAADirty(targetUpdateBounds);
         }
-        GrTextureProxy* textureProxy = fTarget->asTextureProxy();
+        GrTextureProxy* textureProxy = fTargetView.asTextureProxy();
         if (textureProxy && GrMipMapped::kYes == textureProxy->mipMapped()) {
             textureProxy->markMipMapsDirty();
         }
@@ -161,7 +168,9 @@
         if (!fTextureResolveTask) {
             fTextureResolveTask = textureResolveManager.newTextureResolveRenderTask(caps);
         }
-        fTextureResolveTask->addProxy(sk_ref_sp(dependedOn), resolveFlags, caps);
+        fTextureResolveTask->addProxy(
+                GrSurfaceProxyView(sk_ref_sp(dependedOn), dependedOn->origin(), GrSwizzle()),
+                resolveFlags, caps);
 
         // addProxy() should have closed the texture proxy's previous task.
         SkASSERT(!dependedOnTask || dependedOnTask->isClosed());
@@ -244,30 +253,16 @@
 
 bool GrRenderTask::isInstantiated() const {
     // Some renderTasks (e.g. GrTransferFromRenderTask) don't have a target.
-    if (!fTarget) {
+    GrSurfaceProxy* proxy = fTargetView.proxy();
+    if (!proxy) {
         return true;
     }
 
-    if (!fTarget->isInstantiated()) {
+    if (!proxy->isInstantiated()) {
         return false;
     }
 
-    int minStencilSampleCount = (fTarget->asRenderTargetProxy())
-            ? fTarget->asRenderTargetProxy()->numStencilSamples()
-            : 0;
-
-    if (minStencilSampleCount) {
-        GrRenderTarget* rt = fTarget->peekRenderTarget();
-        SkASSERT(rt);
-
-        GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
-        if (!stencil) {
-            return false;
-        }
-        SkASSERT(stencil->numSamples() >= minStencilSampleCount);
-    }
-
-    GrSurface* surface = fTarget->peekSurface();
+    GrSurface* surface = proxy->peekSurface();
     if (surface->wasDestroyed()) {
         return false;
     }
@@ -278,10 +273,11 @@
 #ifdef SK_DEBUG
 void GrRenderTask::dump(bool printDependencies) const {
     SkDebugf("--------------------------------------------------------------\n");
+    GrSurfaceProxy* proxy = fTargetView.proxy();
     SkDebugf("renderTaskID: %d - proxyID: %d - surfaceID: %d\n", fUniqueID,
-             fTarget ? fTarget->uniqueID().asUInt() : -1,
-             fTarget && fTarget->peekSurface()
-                     ? fTarget->peekSurface()->uniqueID().asUInt()
+             proxy ? proxy->uniqueID().asUInt() : -1,
+             proxy && proxy->peekSurface()
+                     ? proxy->peekSurface()->uniqueID().asUInt()
                      : -1);
 
     if (printDependencies) {
diff --git a/src/gpu/GrRenderTask.h b/src/gpu/GrRenderTask.h
index 3fb3a18..15e5ffd 100644
--- a/src/gpu/GrRenderTask.h
+++ b/src/gpu/GrRenderTask.h
@@ -11,6 +11,7 @@
 #include "include/core/SkRefCnt.h"
 #include "include/private/SkColorData.h"
 #include "include/private/SkTDArray.h"
+#include "src/gpu/GrSurfaceProxyView.h"
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/GrTextureResolveManager.h"
 
@@ -24,7 +25,8 @@
 // contents. (e.g., an opsTask that executes a command buffer, a task to regenerate mipmaps, etc.)
 class GrRenderTask : public SkRefCnt {
 public:
-    GrRenderTask(sk_sp<GrSurfaceProxy> target);
+    GrRenderTask();
+    GrRenderTask(GrSurfaceProxyView);
     ~GrRenderTask() override;
 
     void makeClosed(const GrCaps&);
@@ -74,14 +76,12 @@
 
     virtual int numClips() const { return 0; }
 
-    using VisitSurfaceProxyFunc = std::function<void(GrSurfaceProxy*, GrMipMapped)>;
+    virtual void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const = 0;
 
-    virtual void visitProxies_debugOnly(const VisitSurfaceProxyFunc&) const = 0;
-
-    void visitTargetAndSrcProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const {
+    void visitTargetAndSrcProxies_debugOnly(const GrOp::VisitProxyFunc& fn) const {
         this->visitProxies_debugOnly(fn);
-        if (fTarget) {
-            fn(fTarget.get(), GrMipMapped::kNo);
+        if (fTargetView.proxy()) {
+            fn(fTargetView.proxy(), GrMipMapped::kNo);
         }
     }
 #endif
@@ -105,7 +105,7 @@
     // targetUpdateBounds must not extend beyond the proxy bounds.
     virtual ExpectedOutcome onMakeClosed(const GrCaps&, SkIRect* targetUpdateBounds) = 0;
 
-    sk_sp<GrSurfaceProxy> fTarget;
+    GrSurfaceProxyView fTargetView;
 
     // List of texture proxies whose contents are being prepared on a worker thread
     // TODO: this list exists so we can fire off the proper upload when an renderTask begins
@@ -117,14 +117,14 @@
     friend class GrDrawingManager;
 
     // Drops any pending operations that reference proxies that are not instantiated.
-    // NOTE: Derived classes don't need to check fTarget. That is handled when the drawingManager
-    // calls isInstantiated.
+    // NOTE: Derived classes don't need to check fTargetView. That is handled when the
+    // drawingManager calls isInstantiated.
     virtual void handleInternalAllocationFailure() = 0;
 
     virtual bool onIsUsed(GrSurfaceProxy*) const = 0;
 
     bool isUsed(GrSurfaceProxy* proxy) const {
-        if (proxy == fTarget.get()) {
+        if (proxy == fTargetView.proxy()) {
             return true;
         }
 
diff --git a/src/gpu/GrResourceAllocator.cpp b/src/gpu/GrResourceAllocator.cpp
index 576dfc4..110e38b 100644
--- a/src/gpu/GrResourceAllocator.cpp
+++ b/src/gpu/GrResourceAllocator.cpp
@@ -76,19 +76,6 @@
     SkASSERT(!fAssigned);  // We shouldn't be adding any intervals after (or during) assignment
 
     if (proxy->canSkipResourceAllocator()) {
-        // If the proxy is still not instantiated at this point but will need stencil, it will
-        // attach its own stencil buffer upon onFlush instantiation.
-        if (proxy->isInstantiated()) {
-            auto rt = proxy->asRenderTargetProxy();
-            int minStencilSampleCount = rt ? rt->numStencilSamples() : 0;
-            if (minStencilSampleCount) {
-                if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
-                        fResourceProvider, proxy->peekSurface(), minStencilSampleCount)) {
-                    SkDebugf("WARNING: failed to attach stencil buffer. "
-                             "Rendering may be incorrect.\n");
-                }
-            }
-        }
         return;
     }
 
@@ -265,19 +252,11 @@
 
 // First try to reuse one of the recently allocated/used GrSurfaces in the free pool.
 // If we can't find a useable one, create a new one.
-sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(const GrSurfaceProxy* proxy,
-                                                     int minStencilSampleCount) {
-
+sk_sp<GrSurface> GrResourceAllocator::findSurfaceFor(const GrSurfaceProxy* proxy) {
     if (proxy->asTextureProxy() && proxy->asTextureProxy()->getUniqueKey().isValid()) {
         // First try to reattach to a cached version if the proxy is uniquely keyed
-        sk_sp<GrSurface> surface = fResourceProvider->findByUniqueKey<GrSurface>(
-                                                        proxy->asTextureProxy()->getUniqueKey());
-        if (surface) {
-            if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider, surface.get(),
-                                                           minStencilSampleCount)) {
-                return nullptr;
-            }
-
+        if (sk_sp<GrSurface> surface = fResourceProvider->findByUniqueKey<GrSurface>(
+                proxy->asTextureProxy()->getUniqueKey())) {
             return surface;
         }
     }
@@ -298,11 +277,6 @@
             // match budgeted proxies w/ budgeted surfaces and unbudgeted w/ unbudgeted.
             surface->resourcePriv().makeBudgeted();
         }
-
-        if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(fResourceProvider, surface.get(),
-                                                       minStencilSampleCount)) {
-            return nullptr;
-        }
         SkASSERT(!surface->getUniqueKey().isValid());
         return surface;
     }
@@ -405,16 +379,7 @@
 
         this->expire(cur->start());
 
-        int minStencilSampleCount = (cur->proxy()->asRenderTargetProxy())
-                ? cur->proxy()->asRenderTargetProxy()->numStencilSamples()
-                : 0;
-
         if (cur->proxy()->isInstantiated()) {
-            if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
-                        fResourceProvider, cur->proxy()->peekSurface(), minStencilSampleCount)) {
-                *outError = AssignError::kFailedProxyInstantiation;
-            }
-
             fActiveIntvls.insertByIncreasingEnd(cur);
 
             if (fResourceProvider->overBudget()) {
@@ -432,8 +397,7 @@
             if (!cur->proxy()->priv().doLazyInstantiation(fResourceProvider)) {
                 *outError = AssignError::kFailedProxyInstantiation;
             }
-        } else if (sk_sp<GrSurface> surface =
-                           this->findSurfaceFor(cur->proxy(), minStencilSampleCount)) {
+        } else if (sk_sp<GrSurface> surface = this->findSurfaceFor(cur->proxy())) {
             // TODO: make getUniqueKey virtual on GrSurfaceProxy
             GrTextureProxy* texProxy = cur->proxy()->asTextureProxy();
 
diff --git a/src/gpu/GrResourceAllocator.h b/src/gpu/GrResourceAllocator.h
index 6108b54..da3c735 100644
--- a/src/gpu/GrResourceAllocator.h
+++ b/src/gpu/GrResourceAllocator.h
@@ -121,7 +121,7 @@
 
     // These two methods wrap the interactions with the free pool
     void recycleSurface(sk_sp<GrSurface> surface);
-    sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy, int minStencilSampleCount);
+    sk_sp<GrSurface> findSurfaceFor(const GrSurfaceProxy* proxy);
 
     struct FreePoolTraits {
         static const GrScratchKey& GetKey(const GrSurface& s) {
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 3747638..3068b9b 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -391,16 +391,57 @@
     return buffer;
 }
 
-static constexpr int kMaxQuads = 1 << 12;  // max possible: (1 << 14) - 1;
+///////////////////////////////////////////////////////////////////////////////////////////////////
+static constexpr int kMaxNumNonAAQuads = 1 << 12;  // max possible: (1 << 14) - 1;
+static const int kVertsPerNonAAQuad = 4;
+static const int kIndicesPerNonAAQuad = 6;
 
-sk_sp<const GrGpuBuffer> GrResourceProvider::createQuadIndexBuffer() {
-    GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
-    static const uint16_t kPattern[] = { 0, 1, 2, 2, 1, 3 };
-    return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, nullptr);
+sk_sp<const GrGpuBuffer> GrResourceProvider::createNonAAQuadIndexBuffer() {
+    GR_STATIC_ASSERT(kVertsPerNonAAQuad * kMaxNumNonAAQuads <= 65535); // indices fit in a uint16_t
+
+    static const uint16_t kNonAAQuadIndexPattern[] = {
+        0, 1, 2, 2, 1, 3
+    };
+
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kNonAAQuadIndexPattern) == kIndicesPerNonAAQuad);
+
+    return this->createPatternedIndexBuffer(kNonAAQuadIndexPattern, kIndicesPerNonAAQuad,
+                                            kMaxNumNonAAQuads, kVertsPerNonAAQuad, nullptr);
 }
 
-int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; }
+int GrResourceProvider::MaxNumNonAAQuads() { return kMaxNumNonAAQuads; }
+int GrResourceProvider::NumVertsPerNonAAQuad() { return kVertsPerNonAAQuad; }
+int GrResourceProvider::NumIndicesPerNonAAQuad() { return kIndicesPerNonAAQuad; }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+static constexpr int kMaxNumAAQuads = 1 << 9;  // max possible: (1 << 13) - 1;
+static const int kVertsPerAAQuad = 8;
+static const int kIndicesPerAAQuad = 30;
+
+sk_sp<const GrGpuBuffer> GrResourceProvider::createAAQuadIndexBuffer() {
+    GR_STATIC_ASSERT(kVertsPerAAQuad * kMaxNumAAQuads <= 65535); // indices fit in a uint16_t
+
+    // clang-format off
+    static const uint16_t kAAQuadIndexPattern[] = {
+        0, 1, 2, 1, 3, 2,
+        0, 4, 1, 4, 5, 1,
+        0, 6, 4, 0, 2, 6,
+        2, 3, 6, 3, 7, 6,
+        1, 5, 3, 3, 5, 7,
+    };
+    // clang-format on
+
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kAAQuadIndexPattern) == kIndicesPerAAQuad);
+
+    return this->createPatternedIndexBuffer(kAAQuadIndexPattern, kIndicesPerAAQuad,
+                                            kMaxNumAAQuads, kVertsPerAAQuad, nullptr);
+}
+
+int GrResourceProvider::MaxNumAAQuads() { return kMaxNumAAQuads; }
+int GrResourceProvider::NumVertsPerAAQuad() { return kVertsPerAAQuad; }
+int GrResourceProvider::NumIndicesPerAAQuad() { return kIndicesPerAAQuad; }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
     if (this->isAbandoned()) {
         return nullptr;
@@ -440,10 +481,10 @@
     return buffer;
 }
 
-bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, int minStencilSampleCount) {
+bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt, int numStencilSamples) {
     SkASSERT(rt);
     GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment();
-    if (stencil && stencil->numSamples() >= minStencilSampleCount) {
+    if (stencil && stencil->numSamples() == numStencilSamples) {
         return true;
     }
 
@@ -459,12 +500,12 @@
         }
 #endif
         GrStencilAttachment::ComputeSharedStencilAttachmentKey(
-                width, height, minStencilSampleCount, &sbKey);
+                width, height, numStencilSamples, &sbKey);
         auto stencil = this->findByUniqueKey<GrStencilAttachment>(sbKey);
         if (!stencil) {
             // Need to try and create a new stencil
             stencil.reset(this->gpu()->createStencilAttachmentForRenderTarget(
-                    rt, width, height, minStencilSampleCount));
+                    rt, width, height, numStencilSamples));
             if (!stencil) {
                 return false;
             }
@@ -474,7 +515,7 @@
     }
 
     if (GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment()) {
-        return stencil->numSamples() >= minStencilSampleCount;
+        return stencil->numSamples() == numStencilSamples;
     }
     return false;
 }
@@ -488,13 +529,15 @@
     return fGpu->wrapBackendTextureAsRenderTarget(tex, sampleCnt, colorType);
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(bool isOwned) {
-    return fGpu->makeSemaphore(isOwned);
+std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(
+        bool isOwned) {
+    return this->isAbandoned() ? nullptr : fGpu->makeSemaphore(isOwned);
 }
 
-sk_sp<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                            SemaphoreWrapType wrapType,
-                                                            GrWrapOwnership ownership) {
+std::unique_ptr<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(
+        const GrBackendSemaphore& semaphore,
+        SemaphoreWrapType wrapType,
+        GrWrapOwnership ownership) {
     ASSERT_SINGLE_OWNER
     return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore,
                                                                       wrapType,
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 1d6230f..976c130 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -194,20 +194,40 @@
     }
 
     /**
-     * Returns an index buffer that can be used to render quads.
-     * Six indices per quad: 0, 1, 2, 2, 1, 3, etc.
-     * The max number of quads is the buffer's index capacity divided by 6.
+     * Returns an index buffer that can be used to render non-antialiased quads.
+     * Each quad consumes 6 indices (0, 1, 2, 2, 1, 3) and 4 vertices.
+     * Call MaxNumNonAAQuads to get the max allowed number of non-AA quads.
      * Draw with GrPrimitiveType::kTriangles
-     * @ return the quad index buffer
+     * @ return the non-AA quad index buffer
      */
-    sk_sp<const GrGpuBuffer> refQuadIndexBuffer() {
-        if (!fQuadIndexBuffer) {
-            fQuadIndexBuffer = this->createQuadIndexBuffer();
+    sk_sp<const GrGpuBuffer> refNonAAQuadIndexBuffer() {
+        if (!fNonAAQuadIndexBuffer) {
+            fNonAAQuadIndexBuffer = this->createNonAAQuadIndexBuffer();
         }
-        return fQuadIndexBuffer;
+        return fNonAAQuadIndexBuffer;
     }
 
-    static int QuadCountOfQuadBuffer();
+    static int MaxNumNonAAQuads();
+    static int NumVertsPerNonAAQuad();
+    static int NumIndicesPerNonAAQuad();
+
+    /**
+     * Returns an index buffer that can be used to render antialiased quads.
+     * Each quad consumes 30 indices and 8 vertices.
+     * Call MaxNumAAQuads to get the max allowed number of AA quads.
+     * Draw with GrPrimitiveType::kTriangles
+     * @ return the AA quad index buffer
+     */
+    sk_sp<const GrGpuBuffer> refAAQuadIndexBuffer() {
+        if (!fAAQuadIndexBuffer) {
+            fAAQuadIndexBuffer = this->createAAQuadIndexBuffer();
+        }
+        return fAAQuadIndexBuffer;
+    }
+
+    static int MaxNumAAQuads();
+    static int NumVertsPerAAQuad();
+    static int NumIndicesPerAAQuad();
 
     /**
      * Factories for GrPath objects. It's an error to call these if path rendering
@@ -254,16 +274,16 @@
      */
     void assignUniqueKeyToResource(const GrUniqueKey&, GrGpuResource*);
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true);
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true);
 
     enum class SemaphoreWrapType {
         kWillSignal,
         kWillWait,
     };
 
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore&,
-                                            SemaphoreWrapType wrapType,
-                                            GrWrapOwnership = kBorrow_GrWrapOwnership);
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore&,
+                                                      SemaphoreWrapType wrapType,
+                                                      GrWrapOwnership = kBorrow_GrWrapOwnership);
 
     void abandon() {
         fCache = nullptr;
@@ -346,12 +366,14 @@
                                                         int vertCount,
                                                         const GrUniqueKey* key);
 
-    sk_sp<const GrGpuBuffer> createQuadIndexBuffer();
+    sk_sp<const GrGpuBuffer> createNonAAQuadIndexBuffer();
+    sk_sp<const GrGpuBuffer> createAAQuadIndexBuffer();
 
     GrResourceCache* fCache;
     GrGpu* fGpu;
     sk_sp<const GrCaps> fCaps;
-    sk_sp<const GrGpuBuffer> fQuadIndexBuffer;
+    sk_sp<const GrGpuBuffer> fNonAAQuadIndexBuffer;
+    sk_sp<const GrGpuBuffer> fAAQuadIndexBuffer;
 
     // In debug builds we guard against improper thread handling
     SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
diff --git a/src/gpu/GrScissorState.h b/src/gpu/GrScissorState.h
index 79ba47f..dac735b 100644
--- a/src/gpu/GrScissorState.h
+++ b/src/gpu/GrScissorState.h
@@ -30,7 +30,10 @@
     bool operator!=(const GrScissorState& other) const { return !(*this == other); }
 
     bool enabled() const { return fEnabled; }
-    const SkIRect& rect() const { return fRect; }
+    const SkIRect& rect() const {
+        SkASSERT(fEnabled);
+        return fRect;
+    }
 
 private:
     bool    fEnabled;
diff --git a/src/gpu/GrSemaphore.h b/src/gpu/GrSemaphore.h
index 858cf63..77e5b79 100644
--- a/src/gpu/GrSemaphore.h
+++ b/src/gpu/GrSemaphore.h
@@ -18,22 +18,21 @@
  * along with other resources. If more cases like this arise we could consider moving some of the
  * unused functionality off of GrGpuResource.
  */
-class GrSemaphore : public GrGpuResource {
+class GrSemaphore {
 public:
+    virtual ~GrSemaphore() {}
+
     // The derived class can return its GrBackendSemaphore. This is used when flushing with signal
     // semaphores so we can set the client's GrBackendSemaphore object after we've created the
     // internal semaphore.
     virtual GrBackendSemaphore backendSemaphore() const = 0;
 
-    const char* getResourceType() const override { return "semaphore"; }
-
-protected:
-    explicit GrSemaphore(GrGpu* gpu) : INHERITED(gpu) {}
-
 private:
-    size_t onGpuMemorySize() const override { return 0; }
-
-    typedef GrGpuResource INHERITED;
+    friend class GrGpu; // for setIsOwned
+    // This is only used in GrGpu to handle the case where we created a semaphore that was meant to
+    // be borrowed, but we failed to submit it. So we must go back and switch the semaphore to owned
+    // so that it gets deleted.
+    virtual void setIsOwned() = 0;
 };
 
 #endif
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index b2be03b..60ea381 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -43,11 +43,11 @@
     fRemovePowWithConstantExponent = false;
     fMustWriteToFragColor = false;
     fNoDefaultPrecisionForExternalSamplers = false;
+    fCanOnlyUseSampleMaskWithStencil = false;
     fFlatInterpolationSupport = false;
     fPreferFlatInterpolation = false;
     fNoPerspectiveInterpolationSupport = false;
-    fSampleVariablesSupport = false;
-    fSampleVariablesStencilSupport = false;
+    fSampleMaskSupport = false;
     fExternalTextureSupport = false;
     fVertexIDSupport = false;
     fFPManipulationSupport = false;
@@ -120,12 +120,11 @@
     writer->appendBool("Must write to sk_FragColor [workaround]", fMustWriteToFragColor);
     writer->appendBool("Don't add default precision statement for samplerExternalOES",
                        fNoDefaultPrecisionForExternalSamplers);
+    writer->appendBool("Can only use sample mask with stencil", fCanOnlyUseSampleMaskWithStencil);
     writer->appendBool("Flat interpolation support", fFlatInterpolationSupport);
     writer->appendBool("Prefer flat interpolation", fPreferFlatInterpolation);
     writer->appendBool("No perspective interpolation support", fNoPerspectiveInterpolationSupport);
-    writer->appendBool("Sample variables support", fSampleVariablesSupport);
-    writer->appendBool("Sample variables stencil support [workaround]",
-                       fSampleVariablesStencilSupport);
+    writer->appendBool("Sample mask support", fSampleMaskSupport);
     writer->appendBool("External texture support", fExternalTextureSupport);
     writer->appendBool("sk_VertexID support", fVertexIDSupport);
     writer->appendBool("Floating point manipulation support", fFPManipulationSupport);
@@ -167,6 +166,11 @@
         SkASSERT(!fNoDefaultPrecisionForExternalSamplers);
     }
 #if GR_TEST_UTILS
-    fDualSourceBlendingSupport = fDualSourceBlendingSupport && !options.fSuppressDualSourceBlending;
+    if (options.fSuppressDualSourceBlending) {
+        fDualSourceBlendingSupport = false;
+    }
+    if (options.fSuppressGeometryShaders) {
+        fGeometryShaderSupport = false;
+    }
 #endif
 }
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
index fb68ec3..8165b0d 100644
--- a/src/gpu/GrShaderCaps.h
+++ b/src/gpu/GrShaderCaps.h
@@ -70,12 +70,7 @@
 
     bool noperspectiveInterpolationSupport() const { return fNoPerspectiveInterpolationSupport; }
 
-    // Can we use sample variables everywhere?
-    bool sampleVariablesSupport() const { return fSampleVariablesSupport; }
-
-    // Can we use sample variables when rendering to stencil? (This is a workaround for platforms
-    // where sample variables are broken in general, but seem to work when rendering to stencil.)
-    bool sampleVariablesStencilSupport() const { return fSampleVariablesStencilSupport; }
+    bool sampleMaskSupport() const { return fSampleMaskSupport; }
 
     bool externalTextureSupport() const { return fExternalTextureSupport; }
 
@@ -164,6 +159,11 @@
         return fNoDefaultPrecisionForExternalSamplers;
     }
 
+    // The sample mask round rect op draws nothing on several Adreno and Radeon bots. Other ops that
+    // use sample mask while rendering to stencil seem to work fine.
+    // http://skbug.com/8921
+    bool canOnlyUseSampleMaskWithStencil() const { return fCanOnlyUseSampleMaskWithStencil; }
+
     // Returns the string of an extension that must be enabled in the shader to support
     // derivatives. If nullptr is returned then no extension needs to be enabled. Before calling
     // this function, the caller should check that shaderDerivativeSupport exists.
@@ -222,7 +222,7 @@
     }
 
     const char* sampleVariablesExtensionString() const {
-        SkASSERT(this->sampleVariablesSupport() || this->sampleVariablesStencilSupport());
+        SkASSERT(this->sampleMaskSupport());
         return fSampleVariablesExtensionString;
     }
 
@@ -250,8 +250,7 @@
     bool fFlatInterpolationSupport          : 1;
     bool fPreferFlatInterpolation           : 1;
     bool fNoPerspectiveInterpolationSupport : 1;
-    bool fSampleVariablesSupport            : 1;
-    bool fSampleVariablesStencilSupport     : 1;
+    bool fSampleMaskSupport                 : 1;
     bool fExternalTextureSupport            : 1;
     bool fVertexIDSupport                   : 1;
     bool fFPManipulationSupport             : 1;
@@ -282,6 +281,7 @@
     bool fRemovePowWithConstantExponent               : 1;
     bool fMustWriteToFragColor                        : 1;
     bool fNoDefaultPrecisionForExternalSamplers       : 1;
+    bool fCanOnlyUseSampleMaskWithStencil             : 1;
 
     const char* fVersionDeclString;
 
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index 4245e9e..e4ae6c0 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -144,7 +144,6 @@
 
 void GrSoftwarePathRenderer::DrawToTargetWithShapeMask(
         sk_sp<GrTextureProxy> proxy,
-        GrColorType srcColorType,
         GrRenderTargetContext* renderTargetContext,
         GrPaint&& paint,
         const GrUserStencilSettings& userStencilSettings,
@@ -166,7 +165,7 @@
                                               SkIntToScalar(-textureOriginInDeviceSpace.fY));
     maskMatrix.preConcat(viewMatrix);
     paint.addCoverageFragmentProcessor(GrSimpleTextureEffect::Make(
-            std::move(proxy), srcColorType, maskMatrix, GrSamplerState::Filter::kNearest));
+            std::move(proxy), kPremul_SkAlphaType, maskMatrix, GrSamplerState::Filter::kNearest));
     DrawNonAARect(renderTargetContext, std::move(paint), userStencilSettings, clip, SkMatrix::I(),
                   dstRect, invert);
 }
@@ -386,10 +385,9 @@
                           *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix, devClipBounds,
                           unclippedDevShapeBounds);
     }
-    DrawToTargetWithShapeMask(
-            std::move(proxy), GrColorType::kAlpha_8, args.fRenderTargetContext,
-            std::move(args.fPaint), *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
-            SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
+    DrawToTargetWithShapeMask(std::move(proxy), args.fRenderTargetContext, std::move(args.fPaint),
+                              *args.fUserStencilSettings, *args.fClip, *args.fViewMatrix,
+                              SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
 
     return true;
 }
diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h
index d407791..a510c7b 100644
--- a/src/gpu/GrSoftwarePathRenderer.h
+++ b/src/gpu/GrSoftwarePathRenderer.h
@@ -52,7 +52,6 @@
     // space. The 'viewMatrix' will be used to ensure the correct local coords are provided to
     // any fragment processors in the paint.
     static void DrawToTargetWithShapeMask(sk_sp<GrTextureProxy> proxy,
-                                          GrColorType srcColorType,
                                           GrRenderTargetContext* renderTargetContext,
                                           GrPaint&& paint,
                                           const GrUserStencilSettings& userStencilSettings,
diff --git a/src/gpu/GrStencilSettings.cpp b/src/gpu/GrStencilSettings.cpp
index d03165c..f39648c 100644
--- a/src/gpu/GrStencilSettings.cpp
+++ b/src/gpu/GrStencilSettings.cpp
@@ -20,36 +20,36 @@
         0x0000>()
 );
 
-GR_STATIC_ASSERT(kAll_StencilFlags == (gUnused.fFrontFlags[0] & gUnused.fBackFlags[0]));
+GR_STATIC_ASSERT(kAll_StencilFlags == (gUnused.fCWFlags[0] & gUnused.fCCWFlags[0]));
 
 const GrUserStencilSettings& GrUserStencilSettings::kUnused = gUnused;
 
 void GrStencilSettings::reset(const GrUserStencilSettings& user, bool hasStencilClip,
                               int numStencilBits) {
-    uint16_t frontFlags = user.fFrontFlags[hasStencilClip];
-    if (frontFlags & kSingleSided_StencilFlag) {
-        SkASSERT(frontFlags == user.fBackFlags[hasStencilClip]);
-        fFlags = frontFlags;
+    uint16_t cwFlags = user.fCWFlags[hasStencilClip];
+    if (cwFlags & kSingleSided_StencilFlag) {
+        SkASSERT(cwFlags == user.fCCWFlags[hasStencilClip]);
+        fFlags = cwFlags;
         if (!this->isDisabled()) {
-            fFront.reset(user.fFront, hasStencilClip, numStencilBits);
+            fCWFace.reset(user.fCWFace, hasStencilClip, numStencilBits);
         }
         return;
     }
 
-    uint16_t backFlags = user.fBackFlags[hasStencilClip];
-    fFlags = frontFlags & backFlags;
+    uint16_t ccwFlags = user.fCCWFlags[hasStencilClip];
+    fFlags = cwFlags & ccwFlags;
     if (this->isDisabled()) {
         return;
     }
-    if (!(frontFlags & kDisabled_StencilFlag)) {
-        fFront.reset(user.fFront, hasStencilClip, numStencilBits);
+    if (!(cwFlags & kDisabled_StencilFlag)) {
+        fCWFace.reset(user.fCWFace, hasStencilClip, numStencilBits);
     } else {
-        fFront.setDisabled();
+        fCWFace.setDisabled();
     }
-    if (!(backFlags & kDisabled_StencilFlag)) {
-        fBack.reset(user.fBack, hasStencilClip, numStencilBits);
+    if (!(ccwFlags & kDisabled_StencilFlag)) {
+        fCCWFace.reset(user.fCCWFace, hasStencilClip, numStencilBits);
     } else {
-        fBack.setDisabled();
+        fCCWFace.setDisabled();
     }
 }
 
@@ -59,11 +59,11 @@
         return;
     }
     if (!this->isTwoSided()) {
-        memcpy(&fFront, &that.fFront, sizeof(Face));
+        memcpy(&fCWFace, &that.fCWFace, sizeof(Face));
     } else {
-        memcpy(&fFront, &that.fFront, 2 * sizeof(Face));
+        memcpy(&fCWFace, &that.fCWFace, 2 * sizeof(Face));
         GR_STATIC_ASSERT(sizeof(Face) ==
-                         offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
+                         offsetof(GrStencilSettings, fCCWFace) - offsetof(GrStencilSettings, fCWFace));
     }
 }
 
@@ -77,13 +77,13 @@
         return kDisabled_StencilFlag & (fFlags & that.fFlags);
     }
     if (kSingleSided_StencilFlag & (fFlags & that.fFlags)) {
-        return 0 == memcmp(&fFront, &that.fFront, sizeof(Face)); // Both are single sided.
+        return 0 == memcmp(&fCWFace, &that.fCWFace, sizeof(Face)); // Both are single sided.
     } else if (kSingleSided_StencilFlag & (fFlags | that.fFlags)) {
         return false;
     } else {
-        return 0 == memcmp(&fFront, &that.fFront, 2 * sizeof(Face));
+        return 0 == memcmp(&fCWFace, &that.fCWFace, 2 * sizeof(Face));
         GR_STATIC_ASSERT(sizeof(Face) ==
-                         offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
+                         offsetof(GrStencilSettings, fCCWFace) - offsetof(GrStencilSettings, fCWFace));
     }
     // memcmp relies on GrStencilSettings::Face being tightly packed.
     GR_STATIC_ASSERT(0 == offsetof(Face, fRef));
@@ -478,16 +478,16 @@
         constexpr int kCount16 = sizeof(Face) / sizeof(uint16_t);
         GR_STATIC_ASSERT(0 == sizeof(Face) % sizeof(uint16_t));
         uint16_t* key = reinterpret_cast<uint16_t*>(b->add32n((kCount16 + 1) / 2));
-        memcpy(key, &fFront, sizeof(Face));
+        memcpy(key, &fCWFace, sizeof(Face));
         key[kCount16] = 0;
         GR_STATIC_ASSERT(1 == kCount16 % 2);
     } else {
         constexpr int kCount32 = (2 * sizeof(Face)) / sizeof(uint32_t);
         GR_STATIC_ASSERT(0 == (2 * sizeof(Face)) % sizeof(uint32_t));
         uint32_t* key = b->add32n(kCount32);
-        memcpy(key, &fFront, 2 * sizeof(Face));
+        memcpy(key, &fCWFace, 2 * sizeof(Face));
         GR_STATIC_ASSERT(sizeof(Face) ==
-                         offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
+                offsetof(GrStencilSettings, fCCWFace) - offsetof(GrStencilSettings, fCWFace));
     }
     // We rely on GrStencilSettings::Face being tightly packed for the key to be reliable.
     GR_STATIC_ASSERT(0 == offsetof(Face, fRef));
diff --git a/src/gpu/GrStencilSettings.h b/src/gpu/GrStencilSettings.h
index e694236..fc1e66a 100644
--- a/src/gpu/GrStencilSettings.h
+++ b/src/gpu/GrStencilSettings.h
@@ -78,18 +78,24 @@
         void setDisabled();
     };
 
-    const Face& frontAndBack() const {
+    const Face& singleSidedFace() const {
         SkASSERT(!this->isDisabled());
         SkASSERT(!this->isTwoSided());
-        return fFront;
+        return fCWFace;
     }
-    const Face& front(GrSurfaceOrigin origin) const {
+    // Returns the stencil settings for triangles that wind clockwise in "post-origin" space.
+    // (i.e., the space that results after a potential y-axis flip on device space for bottom-left
+    // origins.)
+    const Face& postOriginCWFace(GrSurfaceOrigin origin) const {
         SkASSERT(this->isTwoSided());
-        return (kTopLeft_GrSurfaceOrigin == origin) ? fFront : fBack;
+        return (kTopLeft_GrSurfaceOrigin == origin) ? fCWFace : fCCWFace;
     }
-    const Face& back(GrSurfaceOrigin origin) const {
+    // Returns the stencil settings for triangles that wind counter-clockwise in "post-origin"
+    // space. (i.e., the space that results after a potential y-axis flip on device space for
+    // bottom-left origins.)
+    const Face& postOriginCCWFace(GrSurfaceOrigin origin) const {
         SkASSERT(this->isTwoSided());
-        return (kTopLeft_GrSurfaceOrigin == origin) ? fBack : fFront;
+        return (kTopLeft_GrSurfaceOrigin == origin) ? fCCWFace : fCWFace;
     }
 
     /**
@@ -128,8 +134,8 @@
     enum { kInvalid_PrivateFlag = (kLast_StencilFlag << 1) };
 
     uint32_t   fFlags;
-    Face       fFront;
-    Face       fBack;
+    Face       fCWFace;
+    Face       fCCWFace;
 };
 
 #endif
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
index ead9401..b1707be 100644
--- a/src/gpu/GrSurface.cpp
+++ b/src/gpu/GrSurface.cpp
@@ -21,6 +21,12 @@
                               int colorSamplesPerPixel,
                               GrMipMapped mipMapped,
                               bool binSize) {
+    // For external formats we do not actually know the real size of the resource so we just return
+    // 0 here to indicate this.
+    if (format.textureType() == GrTextureType::kExternal) {
+        return 0;
+    }
+
     size_t colorSize;
 
     if (binSize) {
diff --git a/src/gpu/GrSurfaceContext.cpp b/src/gpu/GrSurfaceContext.cpp
index f427478..e742b7c 100644
--- a/src/gpu/GrSurfaceContext.cpp
+++ b/src/gpu/GrSurfaceContext.cpp
@@ -105,13 +105,12 @@
     // Our tight row bytes may have been changed by clipping.
     tightRowBytes = dstInfo.minRowBytes();
 
-    bool premul   = this->colorInfo().alphaType() == kUnpremul_SkAlphaType &&
-                    dstInfo.alphaType() == kPremul_SkAlphaType;
-    bool unpremul = this->colorInfo().alphaType() == kPremul_SkAlphaType &&
-                    dstInfo.alphaType() == kUnpremul_SkAlphaType;
 
-    bool needColorConversion =
-            SkColorSpaceXformSteps::Required(this->colorInfo().colorSpace(), dstInfo.colorSpace());
+    SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{this->colorInfo(), dstInfo}.flags;
+    bool unpremul            = flags.unpremul,
+         needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
+         premul              = flags.premul;
+
 
     const GrCaps* caps = direct->priv().caps();
     // This is the getImageData equivalent to the canvas2D putImageData fast path. We probably don't
@@ -151,7 +150,7 @@
         if (canvas2DFastPath) {
             fp = direct->priv().createPMToUPMEffect(
                     GrSimpleTextureEffect::Make(sk_ref_sp(srcProxy->asTextureProxy()),
-                                                this->colorInfo().colorType(), SkMatrix::I()));
+                                                this->colorInfo().alphaType(), SkMatrix::I()));
             if (dstInfo.colorType() == GrColorType::kBGRA_8888) {
                 fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA());
                 dstInfo = dstInfo.makeColorType(GrColorType::kRGBA_8888);
@@ -162,7 +161,7 @@
             dstInfo = dstInfo.makeAlphaType(kPremul_SkAlphaType);
         } else {
             fp = GrSimpleTextureEffect::Make(sk_ref_sp(srcProxy->asTextureProxy()),
-                                             this->colorInfo().colorType(), SkMatrix::I());
+                                             this->colorInfo().alphaType(), SkMatrix::I());
         }
         if (!fp) {
             return false;
@@ -264,13 +263,10 @@
     // Our tight row bytes may have been changed by clipping.
     tightRowBytes = srcInfo.minRowBytes();
 
-    bool premul   = this->colorInfo().alphaType() == kPremul_SkAlphaType &&
-                    srcInfo.alphaType() == kUnpremul_SkAlphaType;
-    bool unpremul = this->colorInfo().alphaType() == kUnpremul_SkAlphaType &&
-                    srcInfo.alphaType() == kPremul_SkAlphaType;
-
-    bool needColorConversion =
-            SkColorSpaceXformSteps::Required(srcInfo.colorSpace(), this->colorInfo().colorSpace());
+    SkColorSpaceXformSteps::Flags flags = SkColorSpaceXformSteps{srcInfo, this->colorInfo()}.flags;
+    bool unpremul            = flags.unpremul,
+         needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
+         premul              = flags.premul;
 
     const GrCaps* caps = direct->priv().caps();
 
@@ -345,15 +341,14 @@
         if (this->asRenderTargetContext()) {
             std::unique_ptr<GrFragmentProcessor> fp;
             if (canvas2DFastPath) {
-                fp = direct->priv().createUPMToPMEffect(
-                        GrSimpleTextureEffect::Make(std::move(tempProxy), colorType,
-                                                    SkMatrix::I()));
+                fp = direct->priv().createUPMToPMEffect(GrSimpleTextureEffect::Make(
+                        std::move(tempProxy), alphaType, SkMatrix::I()));
                 // Important: check the original src color type here!
                 if (origSrcInfo.colorType() == GrColorType::kBGRA_8888) {
                     fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::BGRA());
                 }
             } else {
-                fp = GrSimpleTextureEffect::Make(std::move(tempProxy), colorType, SkMatrix::I());
+                fp = GrSimpleTextureEffect::Make(std::move(tempProxy), alphaType, SkMatrix::I());
             }
             if (!fp) {
                 return false;
@@ -430,8 +425,10 @@
         return false;
     }
 
-    return this->drawingManager()->newCopyRenderTask(sk_ref_sp(src), srcRect,
-                                                     this->asSurfaceProxyRef(), dstPoint);
+    // The swizzle doesn't matter for copies and it is not used.
+    return this->drawingManager()->newCopyRenderTask(
+            GrSurfaceProxyView(sk_ref_sp(src), src->origin(), GrSwizzle()), srcRect,
+            this->textureSurfaceView(), dstPoint);
 }
 
 std::unique_ptr<GrRenderTargetContext> GrSurfaceContext::rescale(
@@ -460,10 +457,10 @@
     sk_sp<GrTextureProxy> texProxy = sk_ref_sp(this->asTextureProxy());
     SkCanvas::SrcRectConstraint constraint = SkCanvas::kStrict_SrcRectConstraint;
     GrColorType srcColorType = this->colorInfo().colorType();
+    SkAlphaType srcAlphaType = this->colorInfo().alphaType();
     if (!texProxy) {
-        texProxy = GrSurfaceProxy::Copy(fContext, this->asSurfaceProxy(), srcColorType,
-                                        GrMipMapped::kNo, srcRect, SkBackingFit::kApprox,
-                                        SkBudgeted::kNo);
+        texProxy = GrSurfaceProxy::Copy(fContext, this->asSurfaceProxy(), GrMipMapped::kNo, srcRect,
+                                        SkBackingFit::kApprox, SkBudgeted::kNo);
         if (!texProxy) {
             return nullptr;
         }
@@ -497,8 +494,7 @@
     if (rescaleGamma == SkSurface::kLinear && this->colorInfo().colorSpace() &&
         !this->colorInfo().colorSpace()->gammaIsLinear()) {
         auto cs = this->colorInfo().colorSpace()->makeLinearGamma();
-        auto xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(),
-                                             this->colorInfo().alphaType(), cs.get(),
+        auto xform = GrColorSpaceXform::Make(this->colorInfo().colorSpace(), srcAlphaType, cs.get(),
                                              kPremul_SkAlphaType);
         // We'll fall back to kRGBA_8888 if half float not supported.
         auto linearRTC = fContext->priv().makeDeferredRenderTargetContextWithFallback(
@@ -507,10 +503,11 @@
         if (!linearRTC) {
             return nullptr;
         }
-        linearRTC->drawTexture(GrNoClip(), texProxy, srcColorType, GrSamplerState::Filter::kNearest,
-                               SkBlendMode::kSrc, SK_PMColor4fWHITE, SkRect::Make(srcRect),
-                               SkRect::MakeWH(srcW, srcH), GrAA::kNo, GrQuadAAFlags::kNone,
-                               constraint, SkMatrix::I(), std::move(xform));
+        linearRTC->drawTexture(GrNoClip(), texProxy, srcColorType, srcAlphaType,
+                               GrSamplerState::Filter::kNearest, SkBlendMode::kSrc,
+                               SK_PMColor4fWHITE, SkRect::Make(srcRect), SkRect::MakeWH(srcW, srcH),
+                               GrAA::kNo, GrQuadAAFlags::kNone, constraint, SkMatrix::I(),
+                               std::move(xform));
         texProxy = linearRTC->asTextureProxyRef();
         tempA = std::move(linearRTC);
         srcX = 0;
@@ -571,10 +568,9 @@
             if (srcW != texProxy->width() || srcH != texProxy->height()) {
                 auto domain = GrTextureDomain::MakeTexelDomain(
                         SkIRect::MakeXYWH(srcX, srcY, srcW, srcH), GrTextureDomain::kClamp_Mode);
-                fp = GrBicubicEffect::Make(texProxy, srcColorType, matrix, domain, dir,
-                                           prevAlphaType);
+                fp = GrBicubicEffect::Make(texProxy, matrix, domain, dir, prevAlphaType);
             } else {
-                fp = GrBicubicEffect::Make(texProxy, srcColorType, matrix, dir, prevAlphaType);
+                fp = GrBicubicEffect::Make(texProxy, matrix, dir, prevAlphaType);
             }
             if (xform) {
                 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
@@ -588,8 +584,8 @@
             auto filter = rescaleQuality == kNone_SkFilterQuality ? GrSamplerState::Filter::kNearest
                                                                   : GrSamplerState::Filter::kBilerp;
             auto srcSubset = SkRect::MakeXYWH(srcX, srcY, srcW, srcH);
-            tempB->drawTexture(GrNoClip(), texProxy, srcColorType, filter, SkBlendMode::kSrc,
-                               SK_PMColor4fWHITE, srcSubset, dstRect, GrAA::kNo,
+            tempB->drawTexture(GrNoClip(), texProxy, srcColorType, srcAlphaType, filter,
+                               SkBlendMode::kSrc, SK_PMColor4fWHITE, srcSubset, dstRect, GrAA::kNo,
                                GrQuadAAFlags::kNone, constraint, SkMatrix::I(), std::move(xform));
         }
         texProxy = tempB->asTextureProxyRef();
diff --git a/src/gpu/GrSurfaceContext.h b/src/gpu/GrSurfaceContext.h
index 0d2c4b3..29e5d7f 100644
--- a/src/gpu/GrSurfaceContext.h
+++ b/src/gpu/GrSurfaceContext.h
@@ -40,6 +40,7 @@
 
     const GrColorInfo& colorInfo() const { return fColorInfo; }
     GrSurfaceOrigin origin() const { return fOrigin; }
+    const GrSwizzle& textureSwizzle() const { return fTextureSwizzle; }
     GrSurfaceProxyView textureSurfaceView() {
         return { this->asSurfaceProxyRef(), fOrigin, fTextureSwizzle };
     }
@@ -105,7 +106,6 @@
     }
 #endif
 
-
 protected:
     friend class GrSurfaceContextPriv;
 
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 80f6cd8..7577811 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -129,26 +129,8 @@
     SkASSERT(!fLastRenderTask);
 }
 
-bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
-                                               GrSurface* surface, int minStencilSampleCount) {
-    if (minStencilSampleCount) {
-        GrRenderTarget* rt = surface->asRenderTarget();
-        if (!rt) {
-            SkASSERT(0);
-            return false;
-        }
-
-        if (!resourceProvider->attachStencilAttachment(rt, minStencilSampleCount)) {
-            return false;
-        }
-    }
-
-    return true;
-}
-
 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
                                                    int sampleCnt,
-                                                   int minStencilSampleCount,
                                                    GrRenderable renderable,
                                                    GrMipMapped mipMapped) const {
     SkASSERT(mipMapped == GrMipMapped::kNo || fFit == SkBackingFit::kExact);
@@ -171,11 +153,6 @@
         return nullptr;
     }
 
-    if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(),
-                                                   minStencilSampleCount)) {
-        return nullptr;
-    }
-
     return surface;
 }
 
@@ -204,11 +181,6 @@
 #ifdef SK_DEBUG
     if (this->asRenderTargetProxy()) {
         SkASSERT(fTarget->asRenderTarget());
-        if (int minStencilSampleCount = this->asRenderTargetProxy()->numStencilSamples()) {
-            auto* stencil = fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment();
-            SkASSERT(stencil);
-            SkASSERT(stencil->numSamples() >= minStencilSampleCount);
-        }
     }
 
     if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
@@ -218,19 +190,18 @@
 }
 
 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
-                                     int minStencilSampleCount, GrRenderable renderable,
-                                     GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
+                                     GrRenderable renderable, GrMipMapped mipMapped,
+                                     const GrUniqueKey* uniqueKey) {
     SkASSERT(!this->isLazy());
     if (fTarget) {
         if (uniqueKey && uniqueKey->isValid()) {
             SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
         }
-        return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget.get(),
-                                                         minStencilSampleCount);
+        return true;
     }
 
-    sk_sp<GrSurface> surface = this->createSurfaceImpl(
-            resourceProvider, sampleCnt, minStencilSampleCount, renderable, mipMapped);
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable,
+                                                       mipMapped);
     if (!surface) {
         return false;
     }
@@ -307,7 +278,6 @@
 
 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
                                            GrSurfaceProxy* src,
-                                           GrColorType srcColorType,
                                            GrMipMapped mipMapped,
                                            SkIRect srcRect,
                                            SkBackingFit fit,
@@ -349,8 +319,7 @@
                 fit, width, height, colorType, nullptr, 1, mipMapped, src->origin(), nullptr,
                 budgeted);
 
-        if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcColorType, srcRect,
-                                                  dstPoint)) {
+        if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcRect, dstPoint)) {
             return dstContext->asTextureProxyRef();
         }
     }
@@ -359,11 +328,10 @@
 }
 
 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, GrSurfaceProxy* src,
-                                           GrColorType srcColorType, GrMipMapped mipMapped,
-                                           SkBackingFit fit, SkBudgeted budgeted) {
+                                           GrMipMapped mipMapped, SkBackingFit fit,
+                                           SkBudgeted budgeted) {
     SkASSERT(!src->isFullyLazy());
-    return Copy(context, src, srcColorType, mipMapped, SkIRect::MakeSize(src->dimensions()), fit,
-                budgeted);
+    return Copy(context, src, mipMapped, SkIRect::MakeSize(src->dimensions()), fit, budgeted);
 }
 
 #if GR_TEST_UTILS
@@ -451,14 +419,6 @@
     SkASSERT(fProxy->width() <= surface->width());
     SkASSERT(fProxy->height() <= surface->height());
 
-    auto rt = fProxy->asRenderTargetProxy();
-    int minStencilSampleCount = rt ? rt->numSamples() : 0;
-
-    if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
-            resourceProvider, surface.get(), minStencilSampleCount)) {
-        return false;
-    }
-
     if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
         texProxy->setTargetKeySync(syncKey);
         if (syncKey) {
diff --git a/src/gpu/GrSurfaceProxy.h b/src/gpu/GrSurfaceProxy.h
index 1b0ad12..f4e2d15 100644
--- a/src/gpu/GrSurfaceProxy.h
+++ b/src/gpu/GrSurfaceProxy.h
@@ -284,15 +284,13 @@
 
     // Helper function that creates a temporary SurfaceContext to perform the copy
     // The copy is is not a render target and not multisampled.
-    static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src,
-                                      GrColorType srcColorType, GrMipMapped,
+    static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped,
                                       SkIRect srcRect, SkBackingFit, SkBudgeted,
                                       RectsMustMatch = RectsMustMatch::kNo);
 
     // Copy the entire 'src'
-    static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src,
-                                      GrColorType srcColortype, GrMipMapped, SkBackingFit,
-                                      SkBudgeted);
+    static sk_sp<GrTextureProxy> Copy(GrRecordingContext*, GrSurfaceProxy* src, GrMipMapped,
+                                      SkBackingFit, SkBudgeted);
 
 #if GR_TEST_UTILS
     int32_t testingOnly_getBackingRefCnt() const;
@@ -354,8 +352,8 @@
     virtual sk_sp<GrSurface> createSurface(GrResourceProvider*) const = 0;
     void assign(sk_sp<GrSurface> surface);
 
-    sk_sp<GrSurface> createSurfaceImpl(GrResourceProvider*, int sampleCnt,
-                                       int minStencilSampleCount, GrRenderable, GrMipMapped) const;
+    sk_sp<GrSurface> createSurfaceImpl(GrResourceProvider*, int sampleCnt, GrRenderable,
+                                       GrMipMapped) const;
 
     // Once the dimensions of a fully-lazy proxy are decided, and before it gets instantiated, the
     // client can use this optional method to specify the proxy's dimensions. (A proxy's dimensions
@@ -367,8 +365,8 @@
         fDimensions = dimensions;
     }
 
-    bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
-                         int minStencilSampleCount, GrRenderable, GrMipMapped, const GrUniqueKey*);
+    bool instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, GrRenderable,
+                         GrMipMapped, const GrUniqueKey*);
 
     // For deferred proxies this will be null until the proxy is instantiated.
     // For wrapped proxies it will point to the wrapped resource.
diff --git a/src/gpu/GrSurfaceProxyPriv.h b/src/gpu/GrSurfaceProxyPriv.h
index 7377651..806fc59 100644
--- a/src/gpu/GrSurfaceProxyPriv.h
+++ b/src/gpu/GrSurfaceProxyPriv.h
@@ -38,10 +38,6 @@
 
     bool doLazyInstantiation(GrResourceProvider*);
 
-
-    static bool SK_WARN_UNUSED_RESULT AttachStencilIfNeeded(GrResourceProvider*, GrSurface*,
-                                                            int minStencilSampleCount);
-
 private:
     explicit GrSurfaceProxyPriv(GrSurfaceProxy* proxy) : fProxy(proxy) {}
     GrSurfaceProxyPriv(const GrSurfaceProxyPriv&) {} // unimpl
diff --git a/src/gpu/GrSurfaceProxyView.h b/src/gpu/GrSurfaceProxyView.h
index 9f81713..268f035 100644
--- a/src/gpu/GrSurfaceProxyView.h
+++ b/src/gpu/GrSurfaceProxyView.h
@@ -15,22 +15,51 @@
 
 class GrSurfaceProxyView {
 public:
+    GrSurfaceProxyView() = default;
+
     GrSurfaceProxyView(sk_sp<GrSurfaceProxy> proxy, GrSurfaceOrigin origin, GrSwizzle swizzle)
             : fProxy(proxy), fOrigin(origin), fSwizzle(swizzle) {}
 
-    GrSurfaceProxyView(GrSurfaceProxyView&& view)
-            : fProxy(std::move(view.fProxy)), fOrigin(view.fOrigin), fSwizzle(view.fSwizzle) {}
+    // This entry point is used when we don't care about the origin or the swizzle.
+    GrSurfaceProxyView(sk_sp<GrSurfaceProxy> proxy)
+            : fProxy(proxy), fOrigin(kTopLeft_GrSurfaceOrigin) {}
 
-    GrSurfaceProxy* asSurfaceProxy() const { return fProxy.get(); }
-    GrTextureProxy* asTextureProxy() const { return fProxy->asTextureProxy(); }
-    GrRenderTargetProxy* asRenderTargetProxy() const { return fProxy->asRenderTargetProxy(); }
+    GrSurfaceProxyView(GrSurfaceProxyView&& view) = default;
+    GrSurfaceProxyView(const GrSurfaceProxyView&) = default;
+
+    GrSurfaceProxyView& operator=(const GrSurfaceProxyView&) = default;
+
+    bool operator==(const GrSurfaceProxyView& view) const {
+        return fProxy->uniqueID() == view.fProxy->uniqueID() &&
+               fOrigin == view.fOrigin &&
+               fSwizzle == view.fSwizzle;
+    }
+    bool operator!=(const GrSurfaceProxyView& other) const { return !(*this == other); }
+
+    GrSurfaceProxy* proxy() const { return fProxy.get(); }
+    GrTextureProxy* asTextureProxy() const {
+        if (!fProxy) {
+            return nullptr;
+        }
+        return fProxy->asTextureProxy();
+    }
+    GrRenderTargetProxy* asRenderTargetProxy() const {
+        if (!fProxy) {
+            return nullptr;
+        }
+        return fProxy->asRenderTargetProxy();
+    }
 
     GrSurfaceOrigin origin() const { return fOrigin; }
     const GrSwizzle& swizzle() const { return fSwizzle; }
 
+    void reset() {
+        *this = {};
+    }
+
 private:
     sk_sp<GrSurfaceProxy> fProxy;
-    GrSurfaceOrigin fOrigin;
+    GrSurfaceOrigin fOrigin = kTopLeft_GrSurfaceOrigin;
     GrSwizzle fSwizzle;
 };
 
diff --git a/src/gpu/GrSwizzle.cpp b/src/gpu/GrSwizzle.cpp
index f1b3b85..b52d207 100644
--- a/src/gpu/GrSwizzle.cpp
+++ b/src/gpu/GrSwizzle.cpp
@@ -28,9 +28,20 @@
             // Rather than allocate the 4 control bytes on the heap somewhere, just jam them right
             // into a uintptr_t context.
             uintptr_t ctx;
-            memcpy(&ctx, fSwiz, 4 * sizeof(char));
+            memcpy(&ctx, this->asString().c_str(), 4 * sizeof(char));
             pipeline->append(SkRasterPipeline::swizzle, ctx);
             return;
         }
     }
 }
+
+SkString GrSwizzle::asString() const {
+    char swiz[5];
+    uint16_t key = fKey;
+    for (int i = 0; i < 4; ++i) {
+        swiz[i] = IToC(key & 0xfU);
+        key >>= 4;
+    }
+    swiz[4] = '\0';
+    return SkString(swiz);
+}
diff --git a/src/gpu/GrSwizzle.h b/src/gpu/GrSwizzle.h
index 94562ff..a250842 100644
--- a/src/gpu/GrSwizzle.h
+++ b/src/gpu/GrSwizzle.h
@@ -8,6 +8,7 @@
 #ifndef GrSwizzle_DEFINED
 #define GrSwizzle_DEFINED
 
+#include "include/core/SkString.h"
 #include "include/private/SkColorData.h"
 #include "src/gpu/GrColor.h"
 
@@ -31,11 +32,12 @@
     constexpr uint16_t asKey() const { return fKey; }
 
     /** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a', '0', and '1'. */
-    constexpr const char* c_str() const { return fSwiz; }
+    SkString asString() const;
 
     constexpr char operator[](int i) const {
         SkASSERT(i >= 0 && i < 4);
-        return fSwiz[i];
+        int idx = (fKey >> (4U * i)) & 0xfU;
+        return IToC(idx);
     }
 
     /** Applies this swizzle to the input color and returns the swizzled color. */
@@ -52,29 +54,23 @@
     static constexpr GrSwizzle RGB1() { return GrSwizzle("rgb1"); }
 
 private:
+    explicit constexpr GrSwizzle(uint16_t key) : fKey(key) {}
+
     template <SkAlphaType AlphaType>
     static constexpr float ComponentIndexToFloat(const SkRGBA4f<AlphaType>& color, int idx);
     static constexpr int CToI(char c);
     static constexpr char IToC(int idx);
 
-    char fSwiz[5];
     uint16_t fKey;
 };
 
 constexpr GrSwizzle::GrSwizzle(const char c[4])
-        : fSwiz{c[0], c[1], c[2], c[3], '\0'}
-        , fKey((CToI(c[0]) << 0) | (CToI(c[1]) << 4) | (CToI(c[2]) << 8) | (CToI(c[3]) << 12)) {}
+        : fKey((CToI(c[0]) << 0) | (CToI(c[1]) << 4) | (CToI(c[2]) << 8) | (CToI(c[3]) << 12)) {}
 
 constexpr GrSwizzle::GrSwizzle(const GrSwizzle& that)
-        : fSwiz{that.fSwiz[0], that.fSwiz[1], that.fSwiz[2], that.fSwiz[3], '\0'}
-        , fKey(that.fKey) {}
+        : fKey(that.fKey) {}
 
 constexpr GrSwizzle& GrSwizzle::operator=(const GrSwizzle& that) {
-    fSwiz[0] = that.fSwiz[0];
-    fSwiz[1] = that.fSwiz[1];
-    fSwiz[2] = that.fSwiz[2];
-    fSwiz[3] = that.fSwiz[3];
-    SkASSERT(fSwiz[4] == '\0');
     fKey = that.fKey;
     return *this;
 }
@@ -137,15 +133,16 @@
 }
 
 constexpr GrSwizzle GrSwizzle::Concat(const GrSwizzle& a, const GrSwizzle& b) {
-    char swiz[4]{};
+    uint16_t key = 0;
     for (int i = 0; i < 4; ++i) {
         int idx = (b.fKey >> (4U * i)) & 0xfU;
-        switch (idx) {
-            case CToI('0'): swiz[i] = '0';          break;
-            case CToI('1'): swiz[i] = '1';          break;
-            default:        swiz[i] = a.fSwiz[idx]; break;
+        if (idx != CToI('0') && idx != CToI('1')) {
+            SkASSERT(idx >= 0 && idx < 4);
+            // Get the index value stored in a at location idx.
+            idx = ((a.fKey >> (4U * idx)) & 0xfU);
         }
+        key |= (idx << (4U * i));
     }
-    return GrSwizzle(swiz);
+    return GrSwizzle(key);
 }
 #endif
diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp
index 6f4fd44..863e864 100644
--- a/src/gpu/GrTessellator.cpp
+++ b/src/gpu/GrTessellator.cpp
@@ -861,15 +861,15 @@
     }
 }
 
-inline bool apply_fill_type(SkPath::FillType fillType, int winding) {
+inline bool apply_fill_type(SkPathFillType fillType, int winding) {
     switch (fillType) {
-        case SkPath::kWinding_FillType:
+        case SkPathFillType::kWinding:
             return winding != 0;
-        case SkPath::kEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
             return (winding & 1) != 0;
-        case SkPath::kInverseWinding_FillType:
+        case SkPathFillType::kInverseWinding:
             return winding == 1;
-        case SkPath::kInverseEvenOdd_FillType:
+        case SkPathFillType::kInverseEvenOdd:
             return (winding & 1) == 1;
         default:
             SkASSERT(false);
@@ -877,7 +877,7 @@
     }
 }
 
-inline bool apply_fill_type(SkPath::FillType fillType, Poly* poly) {
+inline bool apply_fill_type(SkPathFillType fillType, Poly* poly) {
     return poly && apply_fill_type(fillType, poly->fWinding);
 }
 
@@ -1699,7 +1699,7 @@
     return polys;
 }
 
-void remove_non_boundary_edges(const VertexList& mesh, SkPath::FillType fillType,
+void remove_non_boundary_edges(const VertexList& mesh, SkPathFillType fillType,
                                SkArenaAlloc& alloc) {
     TESS_LOG("removing non-boundary edges\n");
     EdgeList activeEdges;
@@ -2118,7 +2118,7 @@
     outerMesh->append(outerVertices);
 }
 
-void extract_boundary(EdgeList* boundary, Edge* e, SkPath::FillType fillType, SkArenaAlloc& alloc) {
+void extract_boundary(EdgeList* boundary, Edge* e, SkPathFillType fillType, SkArenaAlloc& alloc) {
     TESS_LOG("\nextracting boundary\n");
     bool down = apply_fill_type(fillType, e->fWinding);
     Vertex* start = down ? e->fTop : e->fBottom;
@@ -2155,7 +2155,7 @@
 // Stage 5b: Extract boundaries from mesh, simplify and stroke them into a new mesh.
 
 void extract_boundaries(const VertexList& inMesh, VertexList* innerVertices,
-                        VertexList* outerVertices, SkPath::FillType fillType,
+                        VertexList* outerVertices, SkPathFillType fillType,
                         Comparator& c, SkArenaAlloc& alloc) {
     remove_non_boundary_edges(inMesh, fillType, alloc);
     for (Vertex* v = inMesh.fHead; v; v = v->fNext) {
@@ -2205,7 +2205,7 @@
 #endif
 }
 
-Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType fillType,
+Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPathFillType fillType,
                         const SkRect& pathBounds, bool antialias, VertexList* outerMesh,
                         SkArenaAlloc& alloc) {
     Comparator c(pathBounds.width() > pathBounds.height() ? Comparator::Direction::kHorizontal
@@ -2260,7 +2260,7 @@
 }
 
 // Stage 6: Triangulate the monotone polygons into a vertex buffer.
-void* polys_to_triangles(Poly* polys, SkPath::FillType fillType, bool emitCoverage, void* data) {
+void* polys_to_triangles(Poly* polys, SkPathFillType fillType, bool emitCoverage, void* data) {
     for (Poly* poly = polys; poly; poly = poly->fNext) {
         if (apply_fill_type(fillType, poly)) {
             data = poly->emit(emitCoverage, data);
@@ -2272,14 +2272,14 @@
 Poly* path_to_polys(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
                     int contourCnt, SkArenaAlloc& alloc, bool antialias, bool* isLinear,
                     VertexList* outerMesh) {
-    SkPath::FillType fillType = path.getFillType();
-    if (SkPath::IsInverseFillType(fillType)) {
+    SkPathFillType fillType = path.getNewFillType();
+    if (SkPathFillType_IsInverse(fillType)) {
         contourCnt++;
     }
     std::unique_ptr<VertexList[]> contours(new VertexList[contourCnt]);
 
     path_to_contours(path, tolerance, clipBounds, contours.get(), alloc, isLinear);
-    return contours_to_polys(contours.get(), contourCnt, path.getFillType(), path.getBounds(),
+    return contours_to_polys(contours.get(), contourCnt, path.getNewFillType(), path.getBounds(),
                              antialias, outerMesh, alloc);
 }
 
@@ -2292,7 +2292,7 @@
     return contourCnt;
 }
 
-int64_t count_points(Poly* polys, SkPath::FillType fillType) {
+int64_t count_points(Poly* polys, SkPathFillType fillType) {
     int64_t count = 0;
     for (Poly* poly = polys; poly; poly = poly->fNext) {
         if (apply_fill_type(fillType, poly) && poly->fCount >= 3) {
@@ -2343,7 +2343,7 @@
     VertexList outerMesh;
     Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, antialias,
                                 isLinear, &outerMesh);
-    SkPath::FillType fillType = antialias ? SkPath::kWinding_FillType : path.getFillType();
+    SkPathFillType fillType = antialias ? SkPathFillType::kWinding : path.getNewFillType();
     int64_t count64 = count_points(polys, fillType);
     if (antialias) {
         count64 += count_outer_mesh_points(outerMesh);
@@ -2381,7 +2381,7 @@
     bool isLinear;
     Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, false, &isLinear,
                                 nullptr);
-    SkPath::FillType fillType = path.getFillType();
+    SkPathFillType fillType = path.getNewFillType();
     int64_t count64 = count_points(polys, fillType);
     if (0 == count64 || count64 > SK_MaxS32) {
         *verts = nullptr;
diff --git a/src/gpu/GrTestUtils.cpp b/src/gpu/GrTestUtils.cpp
index 42aba5e..b348c78 100644
--- a/src/gpu/GrTestUtils.cpp
+++ b/src/gpu/GrTestUtils.cpp
@@ -232,7 +232,7 @@
         gPath[2].lineTo(-50.0f,  31.0f);
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(gPath); i++) {
-            SkASSERT(SkPath::kConvex_Convexity == gPath[i].getConvexity());
+            SkASSERT(SkPathConvexityType::kConvex == gPath[i].getConvexityType());
         }
     }
 
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index cfc2c8d..711edb3 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -58,19 +58,17 @@
 
     GrPaint paint;
 
+    auto fp = GrSimpleTextureEffect::Make(std::move(inputProxy), kUnknown_SkAlphaType,
+                                          SkMatrix::I(), copyParams.fFilter);
     if (needsDomain) {
         const SkRect domain = localRect.makeInset(0.5f, 0.5f);
         // This would cause us to read values from outside the subset. Surely, the caller knows
         // better!
         SkASSERT(copyParams.fFilter != GrSamplerState::Filter::kMipMap);
-        paint.addColorFragmentProcessor(
-            GrTextureDomainEffect::Make(std::move(inputProxy), colorType, SkMatrix::I(), domain,
-                                        GrTextureDomain::kClamp_Mode, copyParams.fFilter));
-    } else {
-        GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
-        paint.addColorTextureProcessor(std::move(inputProxy), colorType, SkMatrix::I(),
-                                       samplerState);
+        fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
+                                  copyParams.fFilter);
     }
+    paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
     copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
@@ -201,21 +199,20 @@
         const GrSamplerState::Filter* filterOrNullForBicubic) {
     SkASSERT(kTightCopy_DomainMode != domainMode);
     bool clampToBorderSupport = fContext->priv().caps()->clampToBorderSupport();
-    GrColorType srcColorType = this->colorType();
+    SkAlphaType srcAlphaType = this->alphaType();
     if (filterOrNullForBicubic) {
+        GrSamplerState::WrapMode wrapMode = fDomainNeedsDecal && clampToBorderSupport
+                                                    ? GrSamplerState::WrapMode::kClampToBorder
+                                                    : GrSamplerState::WrapMode::kClamp;
+        GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
+        auto fp = GrSimpleTextureEffect::Make(std::move(proxy), srcAlphaType, textureMatrix,
+                                              samplerState);
         if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
             GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
                                                                : GrTextureDomain::kClamp_Mode;
-            return GrTextureDomainEffect::Make(std::move(proxy), srcColorType, textureMatrix,
-                                               domain, wrapMode, *filterOrNullForBicubic);
-        } else {
-            GrSamplerState::WrapMode wrapMode =
-                    fDomainNeedsDecal ? GrSamplerState::WrapMode::kClampToBorder
-                                      : GrSamplerState::WrapMode::kClamp;
-            GrSamplerState samplerState(wrapMode, *filterOrNullForBicubic);
-            return GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, textureMatrix,
-                                               samplerState);
+            return GrDomainEffect::Make(std::move(fp), domain, wrapMode, *filterOrNullForBicubic);
         }
+        return fp;
     } else {
         static const GrSamplerState::WrapMode kClampClamp[] = {
                 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
@@ -226,13 +223,13 @@
         if (kDomain_DomainMode == domainMode || (fDomainNeedsDecal && !clampToBorderSupport)) {
             GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
                                          : GrTextureDomain::kClamp_Mode;
-            return GrBicubicEffect::Make(std::move(proxy), srcColorType, textureMatrix, kClampClamp,
-                                         wrapMode, wrapMode, kDir, this->alphaType(),
+            return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp, wrapMode,
+                                         wrapMode, kDir, srcAlphaType,
                                          kDomain_DomainMode == domainMode ? &domain : nullptr);
         } else {
-            return GrBicubicEffect::Make(std::move(proxy), srcColorType, textureMatrix,
+            return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
                                          fDomainNeedsDecal ? kDecalDecal : kClampClamp, kDir,
-                                         this->alphaType());
+                                         srcAlphaType);
         }
     }
 }
diff --git a/src/gpu/GrTextureProxy.cpp b/src/gpu/GrTextureProxy.cpp
index d9c90b7..2d2f766 100644
--- a/src/gpu/GrTextureProxy.cpp
+++ b/src/gpu/GrTextureProxy.cpp
@@ -91,8 +91,8 @@
     if (this->isLazy()) {
         return false;
     }
-    if (!this->instantiateImpl(resourceProvider, 1, /* needsStencil = */ false, GrRenderable::kNo,
-                               fMipMapped, fUniqueKey.isValid() ? &fUniqueKey : nullptr)) {
+    if (!this->instantiateImpl(resourceProvider, 1, GrRenderable::kNo, fMipMapped,
+                               fUniqueKey.isValid() ? &fUniqueKey : nullptr)) {
         return false;
     }
 
@@ -102,9 +102,8 @@
 }
 
 sk_sp<GrSurface> GrTextureProxy::createSurface(GrResourceProvider* resourceProvider) const {
-    sk_sp<GrSurface> surface =
-            this->createSurfaceImpl(resourceProvider, 1,
-                                    /* needsStencil = */ false, GrRenderable::kNo, fMipMapped);
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, 1, GrRenderable::kNo,
+                                                       fMipMapped);
     if (!surface) {
         return nullptr;
     }
@@ -131,11 +130,6 @@
     fTextureProxy->fDeferredUploader.reset();
 }
 
-GrSamplerState::Filter GrTextureProxy::highestFilterMode() const {
-    return this->hasRestrictedSampling() ? GrSamplerState::Filter::kBilerp
-                                         : GrSamplerState::Filter::kMipMap;
-}
-
 GrMipMapped GrTextureProxy::mipMapped() const {
     if (this->isInstantiated()) {
         return this->peekTexture()->texturePriv().mipMapped();
@@ -148,10 +142,17 @@
                                   this->proxyMipMapped(), !this->priv().isExact());
 }
 
-bool GrTextureProxy::ProxiesAreCompatibleAsDynamicState(const GrTextureProxy* first,
-                                                        const GrTextureProxy* second) {
+GrSamplerState::Filter GrTextureProxy::HighestFilterMode(GrTextureType textureType) {
+    return GrTextureTypeHasRestrictedSampling(textureType) ? GrSamplerState::Filter::kBilerp
+                                                           : GrSamplerState::Filter::kMipMap;
+}
+
+bool GrTextureProxy::ProxiesAreCompatibleAsDynamicState(const GrSurfaceProxy* first,
+                                                        const GrSurfaceProxy* second) {
+    // In order to be compatible, the proxies should also have the same texture type. This is
+    // checked explicitly since the GrBackendFormat == operator does not compare texture type
     return first->config() == second->config() &&
-           first->textureType() == second->textureType() &&
+           first->backendFormat().textureType() == second->backendFormat().textureType() &&
            first->backendFormat() == second->backendFormat();
 }
 
diff --git a/src/gpu/GrTextureProxy.h b/src/gpu/GrTextureProxy.h
index 1718794..8c23a58 100644
--- a/src/gpu/GrTextureProxy.h
+++ b/src/gpu/GrTextureProxy.h
@@ -26,8 +26,6 @@
     // Actually instantiate the backing texture, if necessary
     bool instantiate(GrResourceProvider*) override;
 
-    GrSamplerState::Filter highestFilterMode() const;
-
     // If we are instantiated and have a target, return the mip state of that target. Otherwise
     // returns the proxy's mip state from creation time. This is useful for lazy proxies which may
     // claim to not need mips at creation time, but the instantiation happens to give us a mipped
@@ -60,10 +58,15 @@
         return GrTextureTypeHasRestrictedSampling(this->textureType());
     }
 
+    // Returns the highest allowed filter mode for a given texture type
+    static GrSamplerState::Filter HighestFilterMode(const GrTextureType textureType);
+
     // Returns true if the passed in proxies can be used as dynamic state together when flushing
-    // draws to the gpu.
-    static bool ProxiesAreCompatibleAsDynamicState(const GrTextureProxy* first,
-                                                   const GrTextureProxy* second);
+    // draws to the gpu. This accepts GrSurfaceProxy since the information needed is defined on
+    // that type, but this function exists in GrTextureProxy because it's only relevant when the
+    // proxies are being used as textures.
+    static bool ProxiesAreCompatibleAsDynamicState(const GrSurfaceProxy* first,
+                                                   const GrSurfaceProxy* second);
 
     /**
      * Return the texture proxy's unique key. It will be invalid if the proxy doesn't have one.
@@ -170,7 +173,7 @@
     //
     // NOTE: fMipMapsStatus may no longer be equal to fInitialMipMapsStatus by the time the texture
     // is instantiated, since it tracks mipmaps in the time frame in which the DAG is being built.
-    SkDEBUGCODE(const GrMipMapsStatus fInitialMipMapsStatus);
+    SkDEBUGCODE(const GrMipMapsStatus fInitialMipMapsStatus;)
 
     bool             fSyncTargetKey = true;  // Should target's unique key be sync'ed with ours.
 
diff --git a/src/gpu/GrTextureRenderTargetProxy.cpp b/src/gpu/GrTextureRenderTargetProxy.cpp
index 0c1a2cc..6675884 100644
--- a/src/gpu/GrTextureRenderTargetProxy.cpp
+++ b/src/gpu/GrTextureRenderTargetProxy.cpp
@@ -28,7 +28,6 @@
                                                        GrMipMapped mipMapped,
                                                        GrMipMapsStatus mipMapsStatus,
                                                        const GrSwizzle& texSwizzle,
-                                                       const GrSwizzle& outSwizzle,
                                                        SkBackingFit fit,
                                                        SkBudgeted budgeted,
                                                        GrProtected isProtected,
@@ -37,8 +36,8 @@
         : GrSurfaceProxy(format, desc, GrRenderable::kYes, origin, texSwizzle, fit, budgeted,
                          isProtected, surfaceFlags, useAllocator)
         // for now textures w/ data are always wrapped
-        , GrRenderTargetProxy(caps, format, desc, sampleCnt, origin, texSwizzle, outSwizzle, fit,
-                              budgeted, isProtected, surfaceFlags, useAllocator)
+        , GrRenderTargetProxy(caps, format, desc, sampleCnt, origin, texSwizzle, fit, budgeted,
+                              isProtected, surfaceFlags, useAllocator)
         , GrTextureProxy(format, desc, origin, mipMapped, mipMapsStatus, texSwizzle, fit, budgeted,
                          isProtected, surfaceFlags, useAllocator) {
     this->initSurfaceFlags(caps);
@@ -54,7 +53,6 @@
                                                        GrMipMapped mipMapped,
                                                        GrMipMapsStatus mipMapsStatus,
                                                        const GrSwizzle& texSwizzle,
-                                                       const GrSwizzle& outSwizzle,
                                                        SkBackingFit fit,
                                                        SkBudgeted budgeted,
                                                        GrProtected isProtected,
@@ -65,8 +63,8 @@
         // Since we have virtual inheritance, we initialize GrSurfaceProxy directly. Send null
         // callbacks to the texture and RT proxies simply to route to the appropriate constructors.
         , GrRenderTargetProxy(LazyInstantiateCallback(), format, desc, sampleCnt, origin,
-                              texSwizzle, outSwizzle, fit, budgeted, isProtected, surfaceFlags,
-                              useAllocator, WrapsVkSecondaryCB::kNo)
+                              texSwizzle, fit, budgeted, isProtected, surfaceFlags, useAllocator,
+                              WrapsVkSecondaryCB::kNo)
         , GrTextureProxy(LazyInstantiateCallback(), format, desc, origin, mipMapped, mipMapsStatus,
                          texSwizzle, fit, budgeted, isProtected, surfaceFlags, useAllocator) {
     this->initSurfaceFlags(caps);
@@ -78,10 +76,9 @@
 GrTextureRenderTargetProxy::GrTextureRenderTargetProxy(sk_sp<GrSurface> surf,
                                                        GrSurfaceOrigin origin,
                                                        const GrSwizzle& texSwizzle,
-                                                       const GrSwizzle& outSwizzle,
                                                        UseAllocator useAllocator)
         : GrSurfaceProxy(surf, origin, texSwizzle, SkBackingFit::kExact, useAllocator)
-        , GrRenderTargetProxy(surf, origin, texSwizzle, outSwizzle, useAllocator)
+        , GrRenderTargetProxy(surf, origin, texSwizzle, useAllocator)
         , GrTextureProxy(surf, origin, texSwizzle, useAllocator) {
     SkASSERT(surf->asTexture());
     SkASSERT(surf->asRenderTarget());
@@ -126,9 +123,8 @@
 
     const GrUniqueKey& key = this->getUniqueKey();
 
-    if (!this->instantiateImpl(resourceProvider, this->numSamples(), this->numStencilSamples(),
-                               GrRenderable::kYes, this->mipMapped(),
-                               key.isValid() ? &key : nullptr)) {
+    if (!this->instantiateImpl(resourceProvider, this->numSamples(), GrRenderable::kYes,
+                               this->mipMapped(), key.isValid() ? &key : nullptr)) {
         return false;
     }
     if (key.isValid()) {
@@ -143,9 +139,8 @@
 
 sk_sp<GrSurface> GrTextureRenderTargetProxy::createSurface(
                                                     GrResourceProvider* resourceProvider) const {
-    sk_sp<GrSurface> surface =
-            this->createSurfaceImpl(resourceProvider, this->numSamples(), this->numStencilSamples(),
-                                    GrRenderable::kYes, this->mipMapped());
+    sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, this->numSamples(),
+                                                       GrRenderable::kYes, this->mipMapped());
     if (!surface) {
         return nullptr;
     }
diff --git a/src/gpu/GrTextureRenderTargetProxy.h b/src/gpu/GrTextureRenderTargetProxy.h
index 46c273d..68e68f7 100644
--- a/src/gpu/GrTextureRenderTargetProxy.h
+++ b/src/gpu/GrTextureRenderTargetProxy.h
@@ -36,7 +36,6 @@
                                GrMipMapped,
                                GrMipMapsStatus,
                                const GrSwizzle& textureSwizzle,
-                               const GrSwizzle& outputSwizzle,
                                SkBackingFit,
                                SkBudgeted,
                                GrProtected,
@@ -53,7 +52,6 @@
                                GrMipMapped,
                                GrMipMapsStatus,
                                const GrSwizzle& textureSwizzle,
-                               const GrSwizzle& outputSwizzle,
                                SkBackingFit,
                                SkBudgeted,
                                GrProtected,
@@ -64,7 +62,6 @@
     GrTextureRenderTargetProxy(sk_sp<GrSurface>,
                                GrSurfaceOrigin,
                                const GrSwizzle& textureSwizzle,
-                               const GrSwizzle& outputSwizzle,
                                UseAllocator);
 
     void initSurfaceFlags(const GrCaps&);
diff --git a/src/gpu/GrTextureResolveRenderTask.cpp b/src/gpu/GrTextureResolveRenderTask.cpp
index e12bbc8..80ef7e9 100644
--- a/src/gpu/GrTextureResolveRenderTask.cpp
+++ b/src/gpu/GrTextureResolveRenderTask.cpp
@@ -17,14 +17,14 @@
 GrTextureResolveRenderTask::~GrTextureResolveRenderTask() {
     for (const auto& resolve : fResolves) {
         // Ensure the proxy doesn't keep hold of a dangling back pointer.
-        resolve.fProxy->setLastRenderTask(nullptr);
+        resolve.fProxyView.proxy()->setLastRenderTask(nullptr);
     }
 }
 
 void GrTextureResolveRenderTask::addProxy(
-        sk_sp<GrSurfaceProxy> proxyHolder, GrSurfaceProxy::ResolveFlags flags, const GrCaps& caps) {
-    fResolves.emplace_back(std::move(proxyHolder), flags);
-    GrSurfaceProxy* proxy = fResolves.back().fProxy.get();
+        GrSurfaceProxyView proxyView, GrSurfaceProxy::ResolveFlags flags, const GrCaps& caps) {
+    fResolves.emplace_back(std::move(proxyView), flags);
+    GrSurfaceProxy* proxy = fResolves.back().fProxyView.proxy();
 
     // Ensure the last render task that operated on the proxy is closed. That's where msaa and
     // mipmaps should have been marked dirty.
@@ -58,7 +58,7 @@
     // manipulate the resolve proxies.
     auto fakeOp = alloc->curOp();
     for (const auto& resolve : fResolves) {
-        alloc->addInterval(resolve.fProxy.get(), fakeOp, fakeOp,
+        alloc->addInterval(resolve.fProxyView.proxy(), fakeOp, fakeOp,
                            GrResourceAllocator::ActualUse::kYes);
     }
     alloc->incOps();
@@ -68,10 +68,11 @@
     // Resolve all msaa back-to-back, before regenerating mipmaps.
     for (const auto& resolve : fResolves) {
         if (GrSurfaceProxy::ResolveFlags::kMSAA & resolve.fFlags) {
+            GrSurfaceProxy* proxy = resolve.fProxyView.proxy();
             // peekRenderTarget might be null if there was an instantiation error.
-            if (GrRenderTarget* renderTarget = resolve.fProxy->peekRenderTarget()) {
+            if (GrRenderTarget* renderTarget = proxy->peekRenderTarget()) {
                 flushState->gpu()->resolveRenderTarget(renderTarget, resolve.fMSAAResolveRect,
-                                                       resolve.fProxy->origin(),
+                                                       resolve.fProxyView.origin(),
                                                        GrGpu::ForExternalIO::kNo);
             }
         }
@@ -80,7 +81,7 @@
     for (const auto& resolve : fResolves) {
         if (GrSurfaceProxy::ResolveFlags::kMipMaps & resolve.fFlags) {
             // peekTexture might be null if there was an instantiation error.
-            GrTexture* texture = resolve.fProxy->peekTexture();
+            GrTexture* texture = resolve.fProxyView.proxy()->peekTexture();
             if (texture && texture->texturePriv().mipMapsAreDirty()) {
                 flushState->gpu()->regenerateMipMapLevels(texture);
                 SkASSERT(!texture->texturePriv().mipMapsAreDirty());
@@ -92,9 +93,9 @@
 }
 
 #ifdef SK_DEBUG
-void GrTextureResolveRenderTask::visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const {
+void GrTextureResolveRenderTask::visitProxies_debugOnly(const GrOp::VisitProxyFunc& fn) const {
     for (const auto& resolve : fResolves) {
-        fn(resolve.fProxy.get(), GrMipMapped::kNo);
+        fn(resolve.fProxyView.proxy(), GrMipMapped::kNo);
     }
 }
 #endif
diff --git a/src/gpu/GrTextureResolveRenderTask.h b/src/gpu/GrTextureResolveRenderTask.h
index 48fea39..9d567d3 100644
--- a/src/gpu/GrTextureResolveRenderTask.h
+++ b/src/gpu/GrTextureResolveRenderTask.h
@@ -12,14 +12,15 @@
 
 class GrTextureResolveRenderTask final : public GrRenderTask {
 public:
-    GrTextureResolveRenderTask() : GrRenderTask(nullptr) {}
+    GrTextureResolveRenderTask() : GrRenderTask() {}
     ~GrTextureResolveRenderTask() override;
 
-    void addProxy(sk_sp<GrSurfaceProxy>, GrSurfaceProxy::ResolveFlags, const GrCaps&);
+    void addProxy(GrSurfaceProxyView proxyView, GrSurfaceProxy::ResolveFlags, const GrCaps&);
 
 private:
     bool onIsUsed(GrSurfaceProxy* proxy) const override {
-        SkASSERT(proxy != fTarget.get());  // This case should be handled by GrRenderTask.
+        // This case should be handled by GrRenderTask.
+        SkASSERT(proxy != fTargetView.proxy());
         return false;
     }
     void handleInternalAllocationFailure() override {
@@ -34,13 +35,13 @@
     bool onExecute(GrOpFlushState*) override;
 
 #ifdef SK_DEBUG
-    SkDEBUGCODE(void visitProxies_debugOnly(const VisitSurfaceProxyFunc&) const override;)
+    SkDEBUGCODE(void visitProxies_debugOnly(const GrOp::VisitProxyFunc&) const override;)
 #endif
 
     struct Resolve {
-        Resolve(sk_sp<GrSurfaceProxy> proxy, GrSurfaceProxy::ResolveFlags flags)
-                : fProxy(std::move(proxy)), fFlags(flags) {}
-        sk_sp<GrSurfaceProxy> fProxy;
+        Resolve(GrSurfaceProxyView proxyView, GrSurfaceProxy::ResolveFlags flags)
+                : fProxyView(std::move(proxyView)), fFlags(flags) {}
+        GrSurfaceProxyView fProxyView;
         GrSurfaceProxy::ResolveFlags fFlags;
         SkIRect fMSAAResolveRect;
     };
diff --git a/src/gpu/GrTransferFromRenderTask.h b/src/gpu/GrTransferFromRenderTask.h
index 40e89da..ce29b97 100644
--- a/src/gpu/GrTransferFromRenderTask.h
+++ b/src/gpu/GrTransferFromRenderTask.h
@@ -18,7 +18,7 @@
                              GrColorType dstColorType,
                              sk_sp<GrGpuBuffer> dstBuffer,
                              size_t dstOffset)
-            : GrRenderTask(nullptr)
+            : GrRenderTask()
             , fSrcProxy(std::move(srcProxy))
             , fSrcRect(srcRect)
             , fSurfaceColorType(surfaceColorType)
@@ -28,7 +28,7 @@
 
 private:
     bool onIsUsed(GrSurfaceProxy* proxy) const override {
-        SkASSERT(!fTarget);
+        SkASSERT(!fTargetView.proxy());
         return proxy == fSrcProxy.get();
     }
     // If fSrcProxy is uninstantiated at flush time we simply will skip doing the transfer.
@@ -42,7 +42,7 @@
     bool onExecute(GrOpFlushState*) override;
 
 #ifdef SK_DEBUG
-    void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {
+    void visitProxies_debugOnly(const GrOp::VisitProxyFunc& fn) const override {
         fn(fSrcProxy.get(), GrMipMapped::kNo);
     }
 #endif
diff --git a/src/gpu/GrUserStencilSettings.h b/src/gpu/GrUserStencilSettings.h
index 83a19b7..95e97bc 100644
--- a/src/gpu/GrUserStencilSettings.h
+++ b/src/gpu/GrUserStencilSettings.h
@@ -118,12 +118,12 @@
     template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask,
              GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask> struct Init {};
 
-    template<uint16_t FtRef,            uint16_t BkRef,
-             GrUserStencilTest FtTest,  GrUserStencilTest BkTest,
-             uint16_t FtTestMask,       uint16_t BkTestMask,
-             GrUserStencilOp FtPassOp,  GrUserStencilOp BkPassOp,
-             GrUserStencilOp FtFailOp,  GrUserStencilOp BkFailOp,
-             uint16_t FtWriteMask,      uint16_t BkWriteMask> struct InitSeparate {};
+    template<uint16_t CWRef,            uint16_t CCWRef,
+             GrUserStencilTest CWTest,  GrUserStencilTest CCWTest,
+             uint16_t CWTestMask,       uint16_t CCWTestMask,
+             GrUserStencilOp CWPassOp,  GrUserStencilOp CCWPassOp,
+             GrUserStencilOp CWFailOp,  GrUserStencilOp CCWFailOp,
+             uint16_t CWWriteMask,      uint16_t CCWWriteMask> struct InitSeparate {};
 
     template<uint16_t Ref, GrUserStencilTest Test, uint16_t TestMask,
              GrUserStencilOp PassOp, GrUserStencilOp FailOp, uint16_t WriteMask>
@@ -131,17 +131,17 @@
         return Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>();
     }
 
-    template<uint16_t FtRef,            uint16_t BkRef,
-             GrUserStencilTest FtTest,  GrUserStencilTest BkTest,
-             uint16_t FtTestMask,       uint16_t BkTestMask,
-             GrUserStencilOp FtPassOp,  GrUserStencilOp BkPassOp,
-             GrUserStencilOp FtFailOp,  GrUserStencilOp BkFailOp,
-             uint16_t FtWriteMask,      uint16_t BkWriteMask>
-    constexpr static InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
-                                  FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask,
-                                  BkWriteMask> StaticInitSeparate() {
-        return InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
-                            FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>();
+    template<uint16_t CWRef,            uint16_t CCWRef,
+             GrUserStencilTest CWTest,  GrUserStencilTest CCWTest,
+             uint16_t CWTestMask,       uint16_t CCWTestMask,
+             GrUserStencilOp CWPassOp,  GrUserStencilOp CCWPassOp,
+             GrUserStencilOp CWFailOp,  GrUserStencilOp CCWFailOp,
+             uint16_t CWWriteMask,      uint16_t CCWWriteMask>
+    constexpr static InitSeparate<CWRef, CCWRef, CWTest, CCWTest, CWTestMask, CCWTestMask,
+                                  CWPassOp, CCWPassOp, CWFailOp, CCWFailOp, CWWriteMask,
+                                  CCWWriteMask> StaticInitSeparate() {
+        return InitSeparate<CWRef, CCWRef, CWTest, CCWTest, CWTestMask, CCWTestMask,
+                            CWPassOp, CCWPassOp, CWFailOp, CCWFailOp, CWWriteMask, CCWWriteMask>();
     }
 
     // We construct with template arguments in order to enforce that the struct be compile-time
@@ -151,40 +151,41 @@
              typename Attrs = Attrs<Test, PassOp, FailOp> >
     constexpr explicit GrUserStencilSettings(
             const Init<Ref, Test, TestMask, PassOp, FailOp, WriteMask>&)
-        : fFrontFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
+        : fCWFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
                       (uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)}
-        , fFront{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
+        , fCWFace{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
                  Attrs::EffectiveWriteMask(WriteMask)}
-        , fBackFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
+        , fCCWFlags{(uint16_t)(Attrs::Flags(false) | kSingleSided_StencilFlag),
                      (uint16_t)(Attrs::Flags(true) | kSingleSided_StencilFlag)}
-        , fBack{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
+        , fCCWFace{Ref, Test, Attrs::EffectiveTestMask(TestMask), PassOp, FailOp,
                 Attrs::EffectiveWriteMask(WriteMask)} {
     }
 
-    template<uint16_t FtRef,            uint16_t BkRef,
-             GrUserStencilTest FtTest,  GrUserStencilTest BkTest,
-             uint16_t FtTestMask,       uint16_t BkTestMask,
-             GrUserStencilOp FtPassOp,  GrUserStencilOp BkPassOp,
-             GrUserStencilOp FtFailOp,  GrUserStencilOp BkFailOp,
-             uint16_t FtWriteMask,      uint16_t BkWriteMask,
-             typename FtAttrs = Attrs<FtTest, FtPassOp, FtFailOp>,
-             typename BkAttrs = Attrs<BkTest, BkPassOp, BkFailOp> >
+    template<uint16_t CWRef,            uint16_t CCWRef,
+             GrUserStencilTest CWTest,  GrUserStencilTest CCWTest,
+             uint16_t CWTestMask,       uint16_t CCWTestMask,
+             GrUserStencilOp CWPassOp,  GrUserStencilOp CCWPassOp,
+             GrUserStencilOp CWFailOp,  GrUserStencilOp CCWFailOp,
+             uint16_t CWWriteMask,      uint16_t CCWWriteMask,
+             typename CWAttrs = Attrs<CWTest, CWPassOp, CWFailOp>,
+             typename CCWAttrs = Attrs<CCWTest, CCWPassOp, CCWFailOp> >
     constexpr explicit GrUserStencilSettings(
-            const InitSeparate<FtRef, BkRef, FtTest, BkTest, FtTestMask, BkTestMask,
-                               FtPassOp, BkPassOp, FtFailOp, BkFailOp, FtWriteMask, BkWriteMask>&)
-        : fFrontFlags{FtAttrs::Flags(false), FtAttrs::Flags(true)}
-        , fFront{FtRef, FtTest, FtAttrs::EffectiveTestMask(FtTestMask), FtPassOp, FtFailOp,
-                 FtAttrs::EffectiveWriteMask(FtWriteMask)}
-        , fBackFlags{BkAttrs::Flags(false), BkAttrs::Flags(true)}
-        , fBack{BkRef, BkTest, BkAttrs::EffectiveTestMask(BkTestMask), BkPassOp, BkFailOp,
-                BkAttrs::EffectiveWriteMask(BkWriteMask)} {}
+            const InitSeparate<CWRef, CCWRef, CWTest, CCWTest, CWTestMask, CCWTestMask,
+                               CWPassOp, CCWPassOp, CWFailOp, CCWFailOp, CWWriteMask,
+                               CCWWriteMask>&)
+        : fCWFlags{CWAttrs::Flags(false), CWAttrs::Flags(true)}
+        , fCWFace{CWRef, CWTest, CWAttrs::EffectiveTestMask(CWTestMask), CWPassOp, CWFailOp,
+                 CWAttrs::EffectiveWriteMask(CWWriteMask)}
+        , fCCWFlags{CCWAttrs::Flags(false), CCWAttrs::Flags(true)}
+        , fCCWFace{CCWRef, CCWTest, CCWAttrs::EffectiveTestMask(CCWTestMask), CCWPassOp, CCWFailOp,
+                CCWAttrs::EffectiveWriteMask(CCWWriteMask)} {}
 
     // This struct can only be constructed with static initializers.
     GrUserStencilSettings() = delete;
     GrUserStencilSettings(const GrUserStencilSettings&) = delete;
 
     uint16_t flags(bool hasStencilClip) const {
-        return fFrontFlags[hasStencilClip] & fBackFlags[hasStencilClip];
+        return fCWFlags[hasStencilClip] & fCCWFlags[hasStencilClip];
     }
     bool isDisabled(bool hasStencilClip) const {
         return this->flags(hasStencilClip) & kDisabled_StencilFlag;
@@ -199,10 +200,10 @@
         return !(this->flags(hasStencilClip) & kNoWrapOps_StencilFlag);
     }
 
-    const uint16_t   fFrontFlags[2]; // frontFlagsForDraw = fFrontFlags[hasStencilClip].
-    const Face       fFront;
-    const uint16_t   fBackFlags[2]; // backFlagsForDraw = fBackFlags[hasStencilClip].
-    const Face       fBack;
+    const uint16_t   fCWFlags[2]; // cwFlagsForDraw = fCWFlags[hasStencilClip].
+    const Face       fCWFace;
+    const uint16_t   fCCWFlags[2]; // ccwFlagsForDraw = fCCWFlags[hasStencilClip].
+    const Face       fCCWFace;
 
     static const GrUserStencilSettings& kUnused;
 
diff --git a/src/gpu/GrWaitRenderTask.cpp b/src/gpu/GrWaitRenderTask.cpp
index a09a9a6..d7b2a7c 100644
--- a/src/gpu/GrWaitRenderTask.cpp
+++ b/src/gpu/GrWaitRenderTask.cpp
@@ -14,15 +14,15 @@
 void GrWaitRenderTask::gatherProxyIntervals(GrResourceAllocator* alloc) const {
     // This renderTask doesn't have "normal" ops. In this case we still need to add an interval (so
     // fEndOfOpsTaskOpIndices will remain in sync), so we create a fake op# to capture the fact that
-    // we manipulate fTarget.
-    alloc->addInterval(fTarget.get(), alloc->curOp(), alloc->curOp(),
+    // we manipulate fTargetView's proxy.
+    alloc->addInterval(fTargetView.proxy(), alloc->curOp(), alloc->curOp(),
                        GrResourceAllocator::ActualUse::kYes);
     alloc->incOps();
 }
 
 bool GrWaitRenderTask::onExecute(GrOpFlushState* flushState) {
     for (int i = 0; i < fNumSemaphores; ++i) {
-        flushState->gpu()->waitSemaphore(fSemaphores[i]);
+        flushState->gpu()->waitSemaphore(fSemaphores[i].get());
     }
     return true;
 }
diff --git a/src/gpu/GrWaitRenderTask.h b/src/gpu/GrWaitRenderTask.h
index fc736e1..f93aade 100644
--- a/src/gpu/GrWaitRenderTask.h
+++ b/src/gpu/GrWaitRenderTask.h
@@ -13,15 +13,17 @@
 
 class GrWaitRenderTask final : public GrRenderTask {
 public:
-    GrWaitRenderTask(sk_sp<GrSurfaceProxy> proxy, std::unique_ptr<sk_sp<GrSemaphore>[]> semaphores,
+    GrWaitRenderTask(GrSurfaceProxyView surfaceView,
+                     std::unique_ptr<std::unique_ptr<GrSemaphore>[]> semaphores,
                      int numSemaphores)
-            : GrRenderTask(std::move(proxy))
+            : GrRenderTask(std::move(surfaceView))
             , fSemaphores(std::move(semaphores))
-            , fNumSemaphores(numSemaphores){}
+            , fNumSemaphores(numSemaphores) {}
 
 private:
     bool onIsUsed(GrSurfaceProxy* proxy) const override {
-        SkASSERT(proxy != fTarget.get());  // This case should be handled by GrRenderTask.
+        // This case should be handled by GrRenderTask.
+        SkASSERT(proxy != fTargetView.proxy());
         return false;
     }
     void handleInternalAllocationFailure() override {}
@@ -35,9 +37,9 @@
 
 #ifdef SK_DEBUG
     // No non-dst proxies.
-    void visitProxies_debugOnly(const VisitSurfaceProxyFunc& fn) const override {}
+    void visitProxies_debugOnly(const GrOp::VisitProxyFunc& fn) const override {}
 #endif
-    std::unique_ptr<sk_sp<GrSemaphore>[]> fSemaphores;
+    std::unique_ptr<std::unique_ptr<GrSemaphore>[]> fSemaphores;
     int fNumSemaphores;
 };
 
diff --git a/src/gpu/GrWindowRectangles.h b/src/gpu/GrWindowRectangles.h
index 1b6bc4d..732c993 100644
--- a/src/gpu/GrWindowRectangles.h
+++ b/src/gpu/GrWindowRectangles.h
@@ -70,7 +70,9 @@
     SkSafeUnref(this->rec());
     fCount = that.fCount;
     if (fCount <= kNumLocalWindows) {
-        memcpy(fLocalWindows, that.fLocalWindows, fCount * sizeof(SkIRect));
+        for (int i = 0; i < fCount; i++) {
+            fLocalWindows[i] = that.fLocalWindows[i];
+        }
     } else {
         fRec = SkRef(that.fRec);
     }
diff --git a/src/gpu/GrXferProcessor.h b/src/gpu/GrXferProcessor.h
index 18525e7..983d272 100644
--- a/src/gpu/GrXferProcessor.h
+++ b/src/gpu/GrXferProcessor.h
@@ -13,6 +13,7 @@
 #include "src/gpu/GrNonAtomicRef.h"
 #include "src/gpu/GrProcessor.h"
 #include "src/gpu/GrProcessorAnalysis.h"
+#include "src/gpu/GrSurfaceProxyView.h"
 
 class GrGLSLXferProcessor;
 class GrProcessorSet;
@@ -53,52 +54,52 @@
      * to the space of the texture. Depending on GPU capabilities a DstTexture may be used by a
      * GrXferProcessor for blending in the fragment shader.
      */
-    class DstProxy {
+    class DstProxyView {
     public:
-        DstProxy() { fOffset.set(0, 0); }
+        DstProxyView() { fOffset.set(0, 0); }
 
-        DstProxy(const DstProxy& other) {
+        DstProxyView(const DstProxyView& other) {
             *this = other;
         }
 
-        DstProxy(sk_sp<GrTextureProxy> proxy, const SkIPoint& offset)
-            : fProxy(std::move(proxy)) {
-            if (fProxy) {
+        DstProxyView(GrSurfaceProxyView view, const SkIPoint& offset)
+            : fProxyView(std::move(view)) {
+            if (fProxyView.proxy()) {
                 fOffset = offset;
             } else {
                 fOffset.set(0, 0);
             }
         }
 
-        DstProxy& operator=(const DstProxy& other) {
-            fProxy = other.fProxy;
+        DstProxyView& operator=(const DstProxyView& other) {
+            fProxyView = other.fProxyView;
             fOffset = other.fOffset;
             return *this;
         }
 
-        bool operator==(const DstProxy& that) const {
-            return fProxy == that.fProxy && fOffset == that.fOffset;
+        bool operator==(const DstProxyView& that) const {
+            return fProxyView == that.fProxyView && fOffset == that.fOffset;
         }
-        bool operator!=(const DstProxy& that) const { return !(*this == that); }
+        bool operator!=(const DstProxyView& that) const { return !(*this == that); }
 
         const SkIPoint& offset() const { return fOffset; }
 
         void setOffset(const SkIPoint& offset) { fOffset = offset; }
         void setOffset(int ox, int oy) { fOffset.set(ox, oy); }
 
-        GrTextureProxy* proxy() const { return fProxy.get(); }
-        sk_sp<GrTextureProxy> refProxy() const { return fProxy; }
+        GrTextureProxy* proxy() const { return fProxyView.asTextureProxy(); }
+        const GrSurfaceProxyView& proxyView() const { return fProxyView; }
 
-        void setProxy(sk_sp<GrTextureProxy> proxy) {
-            fProxy = std::move(proxy);
-            if (!fProxy) {
+        void setProxyView(GrSurfaceProxyView view) {
+            fProxyView = std::move(view);
+            if (!fProxyView.proxy()) {
                 fOffset = {0, 0};
             }
         }
 
     private:
-        sk_sp<GrTextureProxy> fProxy;
-        SkIPoint              fOffset;
+        GrSurfaceProxyView fProxyView;
+        SkIPoint           fOffset;
     };
 
     /**
@@ -250,7 +251,7 @@
 #endif
 class GrXPFactory {
 public:
-    typedef GrXferProcessor::DstProxy DstProxy;
+    typedef GrXferProcessor::DstProxyView DstProxyView;
 
     enum class AnalysisProperties : unsigned {
         kNone = 0x0,
@@ -318,6 +319,6 @@
 #pragma clang diagnostic pop
 #endif
 
-GR_MAKE_BITFIELD_CLASS_OPS(GrXPFactory::AnalysisProperties);
+GR_MAKE_BITFIELD_CLASS_OPS(GrXPFactory::AnalysisProperties)
 
 #endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index abd0237..6d6f1bb 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -230,7 +230,6 @@
 
         SkASSERT(fRenderTargetContext->asTextureProxy());
         SkAssertResult(rtc->blitTexture(fRenderTargetContext->asTextureProxy(),
-                                        fRenderTargetContext->colorInfo().colorType(),
                                         SkIRect::MakeWH(this->width(), this->height()),
                                         SkIPoint::Make(0,0)));
     }
@@ -476,7 +475,7 @@
     path.setIsVolatile(true);
     path.addRRect(outer);
     path.addRRect(inner);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     // TODO: We are losing the possible mutability of the path here but this should probably be
     // fixed by upgrading GrShape to handle DRRects.
@@ -932,7 +931,7 @@
     SkMatrix texMatrix = SkMatrix::MakeRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
     texMatrix.postScale(scales[0], scales[1]);
 
-    GrColorType srcColorType = SkColorTypeToGrColorType(bitmap.colorType());
+    SkAlphaType srcAlphaType = bitmap.alphaType();
 
     // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
     // the rest from the SkPaint.
@@ -955,20 +954,20 @@
         }
         if (bicubic) {
             static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
-            fp = GrBicubicEffect::Make(std::move(proxy), srcColorType, texMatrix, domain, kDir,
-                                       bitmap.alphaType());
+            fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, kDir, srcAlphaType);
         } else {
-            fp = GrTextureDomainEffect::Make(std::move(proxy), srcColorType, texMatrix, domain,
-                                             GrTextureDomain::kClamp_Mode, samplerState.filter());
+            fp = GrSimpleTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix,
+                                             samplerState);
+            fp = GrDomainEffect::Make(std::move(fp), domain, GrTextureDomain::kClamp_Mode,
+                                      samplerState.filter());
         }
     } else if (bicubic) {
         SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());
         GrSamplerState::WrapMode wrapMode[2] = {samplerState.wrapModeX(), samplerState.wrapModeY()};
         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
-        fp = GrBicubicEffect::Make(std::move(proxy), srcColorType, texMatrix, wrapMode, kDir,
-                                   bitmap.alphaType());
+        fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode, kDir, srcAlphaType);
     } else {
-        fp = GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, texMatrix, samplerState);
+        fp = GrSimpleTextureEffect::Make(std::move(proxy), srcAlphaType, texMatrix, samplerState);
     }
 
     fp = GrColorSpaceXformEffect::Make(std::move(fp), bitmap.colorSpace(), bitmap.alphaType(),
@@ -1039,8 +1038,7 @@
 
     tmpUnfiltered.setImageFilter(nullptr);
 
-    GrColorType srcColorType = SkColorTypeToGrColorType(result->colorType());
-    auto fp = GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, SkMatrix::I());
+    auto fp = GrSimpleTextureEffect::Make(std::move(proxy), special->alphaType(), SkMatrix::I());
     fp = GrColorSpaceXformEffect::Make(std::move(fp), result->getColorSpace(), result->alphaType(),
                                        fRenderTargetContext->colorInfo().colorSpace());
     if (GrColorTypeIsAlphaOnly(SkColorTypeToGrColorType(result->colorType()))) {
@@ -1078,8 +1076,8 @@
         std::unique_ptr<GrFragmentProcessor> cfp;
         if (clipProxy && ctm.invert(&inverseClipMatrix)) {
             GrColorType srcColorType = SkColorTypeToGrColorType(clipImage->colorType());
-            cfp = GrSimpleTextureEffect::Make(std::move(clipProxy), srcColorType, inverseClipMatrix,
-                                              sampler);
+            cfp = GrSimpleTextureEffect::Make(std::move(clipProxy), clipImage->alphaType(),
+                                              inverseClipMatrix, sampler);
             if (srcColorType != GrColorType::kAlpha_8) {
                 cfp = GrFragmentProcessor::SwizzleOutput(std::move(cfp), GrSwizzle::AAAA());
             }
@@ -1255,7 +1253,6 @@
         // texture that matches the device contents
         proxy = GrSurfaceProxy::Copy(fContext.get(),
                                      rtc->asSurfaceProxy(),
-                                     rtc->colorInfo().colorType(),
                                      GrMipMapped::kNo,      // Don't auto generate mips
                                      subset,
                                      SkBackingFit::kApprox,
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index caadbf4..72fbbfe 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -220,14 +220,15 @@
         SkPoint srcQuad[4];
         GrMapRectPoints(dstRect, srcRect, dstClip, srcQuad, 4);
 
-        rtc->drawTextureQuad(clip, std::move(proxy), srcColorInfo.colorType(), filter,
-                             paint.getBlendMode(), color, srcQuad, dstClip, aa, aaFlags,
+        rtc->drawTextureQuad(clip, std::move(proxy), srcColorInfo.colorType(),
+                             srcColorInfo.alphaType(), filter, paint.getBlendMode(), color, srcQuad,
+                             dstClip, aa, aaFlags,
                              constraint == SkCanvas::kStrict_SrcRectConstraint ? &srcRect : nullptr,
                              ctm, std::move(textureXform));
     } else {
-        rtc->drawTexture(clip, std::move(proxy), srcColorInfo.colorType(), filter,
-                         paint.getBlendMode(), color, srcRect, dstRect, aa, aaFlags, constraint,
-                         ctm, std::move(textureXform));
+        rtc->drawTexture(clip, std::move(proxy), srcColorInfo.colorType(), srcColorInfo.alphaType(),
+                         filter, paint.getBlendMode(), color, srcRect, dstRect, aa, aaFlags,
+                         constraint, ctm, std::move(textureXform));
     }
 }
 
@@ -424,11 +425,10 @@
     SkBitmap bm;
     if (this->shouldTileImage(image, &src, constraint, paint.getFilterQuality(), ctm, srcToDst)) {
         // only support tiling as bitmap at the moment, so force raster-version
-        if (!as_IB(image)->getROPixels(&bm)) {
+        if (as_IB(image)->getROPixels(&bm)) {
+            this->drawBitmapRect(bm, &src, dst, paint, constraint);
             return;
         }
-        this->drawBitmapRect(bm, &src, dst, paint, constraint);
-        return;
     }
 
     // This is the funnel for all non-tiled bitmap/image draw calls. Log a histogram entry.
@@ -552,8 +552,11 @@
             continue;
         }
 
-        textures[i].fProxy = std::move(proxy);
-        textures[i].fSrcColorType = SkColorTypeToGrColorType(image->colorType());
+        // TODO: have refPinnedTextureProxy and asTextureProxyRef return GrSurfaceProxyViews.
+        GrSurfaceOrigin origin = proxy->origin();
+        const GrSwizzle& swizzle = proxy->textureSwizzle();
+        textures[i].fProxyView = {std::move(proxy), origin, swizzle};
+        textures[i].fSrcAlphaType = image->alphaType();
         textures[i].fSrcRect = set[i].fSrcRect;
         textures[i].fDstRect = set[i].fDstRect;
         textures[i].fDstClipQuad = clip;
@@ -563,8 +566,9 @@
         textures[i].fAAFlags = SkToGrQuadAAFlags(set[i].fAAFlags);
 
         if (n > 0 &&
-            (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState(textures[i].fProxy.get(),
-                                                                 textures[base].fProxy.get()) ||
+            (!GrTextureProxy::ProxiesAreCompatibleAsDynamicState(
+                    textures[i].fProxyView.proxy(),
+                    textures[base].fProxyView.proxy()) ||
              set[i].fImage->alphaType() != set[base].fImage->alphaType() ||
              !SkColorSpace::Equals(set[i].fImage->colorSpace(), set[base].fImage->colorSpace()))) {
             draw();
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index bdb3c3c..0472ee2 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -132,8 +132,8 @@
     if (!ctx->priv().caps()->isFormatCopyable(baseProxy->backendFormat())) {
         return nullptr;
     }
-    return GrSurfaceProxy::Copy(ctx, baseProxy, srcColorType, GrMipMapped::kYes,
-                                SkBackingFit::kExact, SkBudgeted::kYes);
+    return GrSurfaceProxy::Copy(ctx, baseProxy, GrMipMapped::kYes, SkBackingFit::kExact,
+                                SkBudgeted::kYes);
 }
 
 sk_sp<GrTextureProxy> GrRefCachedBitmapTextureProxy(GrRecordingContext* ctx,
@@ -299,6 +299,7 @@
 #ifndef SK_IGNORE_GPU_DITHER
 static inline int32_t dither_range_type_for_config(GrColorType dstColorType) {
     switch (dstColorType) {
+        case GrColorType::kUnknown:
         case GrColorType::kGray_8:
         case GrColorType::kRGBA_8888:
         case GrColorType::kRGB_888x:
@@ -307,12 +308,6 @@
         case GrColorType::kRG_1616:
         case GrColorType::kRGBA_16161616:
         case GrColorType::kRG_F16:
-            return 0;
-        case GrColorType::kBGR_565:
-            return 1;
-        case GrColorType::kABGR_4444:
-            return 2;
-        case GrColorType::kUnknown:
         case GrColorType::kRGBA_8888_SRGB:
         case GrColorType::kRGBA_1010102:
         case GrColorType::kAlpha_F16:
@@ -324,7 +319,16 @@
         case GrColorType::kAlpha_16:
         case GrColorType::kAlpha_F32xxx:
         case GrColorType::kGray_8xxx:
-            return -1;
+        case GrColorType::kRGB_888:
+        case GrColorType::kR_8:
+        case GrColorType::kR_16:
+        case GrColorType::kR_F16:
+        case GrColorType::kGray_F16:
+            return 0;
+        case GrColorType::kBGR_565:
+            return 1;
+        case GrColorType::kABGR_4444:
+            return 2;
     }
     SkUNREACHABLE;
 }
diff --git a/src/gpu/ccpr/GrCCAtlas.cpp b/src/gpu/ccpr/GrCCAtlas.cpp
index d0639bf..d92f6a6 100644
--- a/src/gpu/ccpr/GrCCAtlas.cpp
+++ b/src/gpu/ccpr/GrCCAtlas.cpp
@@ -125,7 +125,7 @@
                             desc, format, GrRenderable::kYes, sampleCount, GrMipMapped::kNo,
                             SkBudgeted::kYes, GrProtected::kNo);
                 }
-                return fBackingTexture;
+                return GrSurfaceProxy::LazyCallbackResult(fBackingTexture);
             },
             fCoverageType, caps, GrSurfaceProxy::UseAllocator::kNo);
 }
diff --git a/src/gpu/ccpr/GrCCAtlas.h b/src/gpu/ccpr/GrCCAtlas.h
index ac7faaa..49a26f6 100644
--- a/src/gpu/ccpr/GrCCAtlas.h
+++ b/src/gpu/ccpr/GrCCAtlas.h
@@ -65,7 +65,7 @@
         SkUNREACHABLE;
     }
 
-    using LazyInstantiateAtlasCallback = std::function<sk_sp<GrTexture>(
+    using LazyInstantiateAtlasCallback = std::function<GrSurfaceProxy::LazyCallbackResult(
             GrResourceProvider*, GrPixelConfig, const GrBackendFormat&, int sampleCount)>;
 
     static sk_sp<GrTextureProxy> MakeLazyAtlasProxy(const LazyInstantiateAtlasCallback&,
diff --git a/src/gpu/ccpr/GrCCClipPath.cpp b/src/gpu/ccpr/GrCCClipPath.cpp
index 9e86d2e..fc0bd9b 100644
--- a/src/gpu/ccpr/GrCCClipPath.cpp
+++ b/src/gpu/ccpr/GrCCClipPath.cpp
@@ -29,7 +29,7 @@
                 if (!textureProxy || !textureProxy->instantiate(resourceProvider)) {
                     fAtlasScale = fAtlasTranslate = {0, 0};
                     SkDEBUGCODE(fHasAtlasTransform = true);
-                    return sk_sp<GrTexture>();
+                    return GrSurfaceProxy::LazyCallbackResult();
                 }
 
                 sk_sp<GrTexture> texture = sk_ref_sp(textureProxy->peekTexture());
@@ -43,7 +43,12 @@
                                     fDevToAtlasOffset.fY * fAtlasScale.y());
                 SkDEBUGCODE(fHasAtlasTransform = true);
 
-                return texture;
+                // We use LazyInstantiationKeyMode::kUnsynced here because CCPR clip masks are never
+                // cached, and the clip FP proxies need to ignore any unique keys that atlas
+                // textures use for path mask caching.
+                return GrSurfaceProxy::LazyCallbackResult(
+                        std::move(texture), true,
+                        GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced);
             },
             atlasCoverageType, caps, GrSurfaceProxy::UseAllocator::kYes);
 
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
index 6abb04e..be90497 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp
@@ -203,12 +203,16 @@
     dynamicStateArrays.fScissorRects = scissorRects;
     GrOpsRenderPass* renderPass = flushState->opsRenderPass();
 
-    GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                              flushState->drawOpArgs().origin(),
-                              pipeline,
-                              *this,
+    GrPrimitiveType primitiveType = this->primType();
+
+    GrProgramInfo programInfo(flushState->proxy()->numSamples(),
+                              flushState->proxy()->numStencilSamples(),
+                              flushState->proxy()->backendFormat(),
+                              flushState->view()->origin(),
+                              &pipeline,
+                              this,
                               nullptr,
-                              &dynamicStateArrays, 0);
+                              &dynamicStateArrays, 0, primitiveType);
 
 
     renderPass->draw(programInfo, meshes, meshCount, drawBounds);
diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h
index 5fdb488..e16227b 100644
--- a/src/gpu/ccpr/GrCCCoverageProcessor.h
+++ b/src/gpu/ccpr/GrCCCoverageProcessor.h
@@ -14,6 +14,7 @@
 #include "src/gpu/GrPipeline.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
+#include "src/gpu/glsl/GrGLSLShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVarying.h"
 
 class GrGLSLFPFragmentBuilder;
@@ -123,6 +124,8 @@
     virtual void draw(GrOpFlushState*, const GrPipeline&, const SkIRect scissorRects[],
                       const GrMesh[], int meshCount, const SkRect& drawBounds) const;
 
+    virtual GrPrimitiveType primType() const = 0;
+
     // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
     // provides details about shape-specific geometry.
     class Shader {
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index 06556c4..2aa6b59 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -17,9 +17,8 @@
 #include "src/gpu/ccpr/GrOctoBounds.h"
 
 static bool has_coord_transforms(const GrPaint& paint) {
-    GrFragmentProcessor::Iter iter(paint);
-    while (const GrFragmentProcessor* fp = iter.next()) {
-        if (!fp->coordTransforms().empty()) {
+    for (const auto& fp : GrFragmentProcessor::PaintCRange(paint)) {
+        if (!fp.coordTransforms().empty()) {
             return true;
         }
     }
@@ -433,10 +432,13 @@
 
     GrPipeline::InitArgs initArgs;
     initArgs.fCaps = &flushState->caps();
-    initArgs.fDstProxy = flushState->drawOpArgs().dstProxy();
+    initArgs.fDstProxyView = flushState->drawOpArgs().dstProxyView();
     initArgs.fOutputSwizzle = flushState->drawOpArgs().outputSwizzle();
     auto clip = flushState->detachAppliedClip();
-    GrPipeline::FixedDynamicState fixedDynamicState(clip.scissorState().rect());
+    GrPipeline::FixedDynamicState fixedDynamicState;
+    if (clip.scissorState().enabled()) {
+        fixedDynamicState.fScissorRect = clip.scissorState().rect();
+    }
     GrPipeline pipeline(initArgs, std::move(fProcessors), std::move(clip));
 
     int baseInstance = fBaseInstance;
@@ -445,16 +447,15 @@
     for (const InstanceRange& range : fInstanceRanges) {
         SkASSERT(range.fEndInstanceIdx > baseInstance);
 
-        const GrTextureProxy* atlas = range.fAtlasProxy;
-        SkASSERT(atlas->isInstantiated());
-
-        GrCCPathProcessor pathProc(
-                range.fCoverageMode, atlas->peekTexture(), atlas->textureSwizzle(), atlas->origin(),
-                fViewMatrixIfUsingLocalCoords);
-        GrTextureProxy* atlasProxy = range.fAtlasProxy;
-        fixedDynamicState.fPrimitiveProcessorTextures = &atlasProxy;
-        pathProc.drawPaths(flushState, pipeline, &fixedDynamicState, *resources, baseInstance,
-                           range.fEndInstanceIdx, this->bounds());
+        GrSurfaceProxy* atlas = range.fAtlasProxy;
+        if (atlas->isInstantiated()) {  // Instantiation can fail in exceptional circumstances.
+            GrCCPathProcessor pathProc(range.fCoverageMode, atlas->peekTexture(),
+                                       atlas->textureSwizzle(), atlas->origin(),
+                                       fViewMatrixIfUsingLocalCoords);
+            fixedDynamicState.fPrimitiveProcessorTextures = &atlas;
+            pathProc.drawPaths(flushState, pipeline, &fixedDynamicState, *resources, baseInstance,
+                               range.fEndInstanceIdx, this->bounds());
+        }
 
         baseInstance = range.fEndInstanceIdx;
     }
diff --git a/src/gpu/ccpr/GrCCFiller.cpp b/src/gpu/ccpr/GrCCFiller.cpp
index 4553c62..a4a3cfd 100644
--- a/src/gpu/ccpr/GrCCFiller.cpp
+++ b/src/gpu/ccpr/GrCCFiller.cpp
@@ -134,12 +134,12 @@
         // We use "winding" fill type right now because we are producing a coverage count, and must
         // fill in every region that has non-zero wind. The path processor will convert coverage
         // count to the appropriate fill type later.
-        fan.setFillType(SkPath::kWinding_FillType);
+        fan.setFillType(SkPathFillType::kWinding);
     } else {
         // When counting winding numbers in the stencil buffer, it works to use even/odd for the fan
         // tessellation (where applicable). But we need to strip out inverse fill info because
         // inverse-ness gets accounted for later on.
-        fan.setFillType(SkPath::ConvertToNonInverseFillType(originalPath.getFillType()));
+        fan.setFillType(SkPathFillType_ConvertToNonInverse(originalPath.getNewFillType()));
     }
     SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
     for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
@@ -204,12 +204,20 @@
         }
 
         int weight = abs(tessWinding);
-        SkASSERT(SkPath::kEvenOdd_FillType != fan.getFillType() || weight == 1);
+        if (SkPathFillType::kEvenOdd == fan.getNewFillType()) {
+            SkASSERT(Algorithm::kCoverageCount != algorithm);  // Covg. count always uses winding.
+            if (weight != 1) {
+                // The tessellator doesn't wrap weights modulo 2 when we request even/odd fill type.
+                SkASSERT(weight & 1);  // Even wind regions are empty and should have been omitted.
+                weight = 1;
+            }
+        }
         if (weight > 1 && Algorithm::kCoverageCount == algorithm) {
             ++newTriangleCounts->fWeightedTriangles;
         } else {
             newTriangleCounts->fTriangles += weight;
         }
+        vertices[i].fWinding = weight;
     }
 
     fFanTessellation.reset(vertices);
@@ -286,7 +294,7 @@
         TriPointInstance::Ordering ordering, TriPointInstance* triPointInstanceData,
         QuadPointInstance* quadPointInstanceData, GrCCFillGeometry::PrimitiveTallies* indices) {
     for (int i = 0; i < numVertices; i += 3) {
-        int weight = abs(vertices[i].fWinding);
+        int weight = vertices[i].fWinding;
         SkASSERT(weight >= 1);
         if (weight > 1 && Algorithm::kStencilWindingCount != fAlgorithm) {
             quadPointInstanceData[indices->fWeightedTriangles++].setW(
diff --git a/src/gpu/ccpr/GrCCPathProcessor.cpp b/src/gpu/ccpr/GrCCPathProcessor.cpp
index 1244953..4c4b300 100644
--- a/src/gpu/ccpr/GrCCPathProcessor.cpp
+++ b/src/gpu/ccpr/GrCCPathProcessor.cpp
@@ -105,12 +105,12 @@
 
 private:
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const auto& proc = primProc.cast<GrCCPathProcessor>();
         pdman.set2f(fAtlasAdjustUniform,
                     1.0f / proc.fAtlasDimensions.fWidth,
                     1.0f / proc.fAtlasDimensions.fHeight);
-        this->setTransformDataHelper(proc.fLocalMatrix, pdman, &transformIter);
+        this->setTransformDataHelper(proc.fLocalMatrix, pdman, transformRange);
     }
 
     GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform;
@@ -141,12 +141,14 @@
                              baseInstance, enablePrimitiveRestart);
     mesh.setVertexData(resources.refVertexBuffer());
 
-    GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                              flushState->drawOpArgs().origin(),
-                              pipeline,
-                              *this,
+    GrProgramInfo programInfo(flushState->proxy()->numSamples(),
+                              flushState->proxy()->numStencilSamples(),
+                              flushState->proxy()->backendFormat(),
+                              flushState->view()->origin(),
+                              &pipeline,
+                              this,
                               fixedDynamicState,
-                              nullptr, 0);
+                              nullptr, 0, primitiveType);
 
     flushState->opsRenderPass()->draw(programInfo, &mesh, 1, bounds);
 }
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index 4174dba..ae9e6d4 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -81,7 +81,7 @@
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
         SkASSERT(fSrcProxy);
-        auto srcProxy = fSrcProxy.get();
+        GrSurfaceProxy* srcProxy = fSrcProxy.get();
         SkASSERT(srcProxy->isInstantiated());
 
         auto coverageMode = GrCCPathProcessor::GetCoverageMode(
diff --git a/src/gpu/ccpr/GrCCStroker.cpp b/src/gpu/ccpr/GrCCStroker.cpp
index cbfa5ae..000e530 100644
--- a/src/gpu/ccpr/GrCCStroker.cpp
+++ b/src/gpu/ccpr/GrCCStroker.cpp
@@ -70,7 +70,7 @@
 // for seamless integration with the connecting geometry.
 class LinearStrokeProcessor : public GrGeometryProcessor {
 public:
-    LinearStrokeProcessor() : GrGeometryProcessor(kLinearStrokeProcessor_ClassID) {
+    LinearStrokeProcessor() : INHERITED(kLinearStrokeProcessor_ClassID) {
         this->setInstanceAttributes(kInstanceAttribs, 2);
 #ifdef SK_DEBUG
         using Instance = LinearStrokeInstance;
@@ -89,13 +89,15 @@
 
     class Impl : public GrGLSLGeometryProcessor {
         void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                     FPCoordTransformIter&&) override {}
+                     const CoordTransformRange&) override {}
         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
     };
 
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
         return new Impl();
     }
+
+    typedef GrGeometryProcessor INHERITED;
 };
 
 void LinearStrokeProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
@@ -180,7 +182,7 @@
 
     class Impl : public GrGLSLGeometryProcessor {
         void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                     FPCoordTransformIter&&) override {}
+                     const CoordTransformRange&) override {}
         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
     };
 
@@ -777,12 +779,14 @@
     GrPipeline::DynamicStateArrays dynamicStateArrays;
     dynamicStateArrays.fScissorRects = fScissorsBuffer.begin();
 
-    GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                              flushState->drawOpArgs().origin(),
-                              pipeline,
-                              processor,
+    GrProgramInfo programInfo(flushState->proxy()->numSamples(),
+                              flushState->proxy()->numStencilSamples(),
+                              flushState->proxy()->backendFormat(),
+                              flushState->view()->origin(),
+                              &pipeline,
+                              &processor,
                               nullptr,
-                              &dynamicStateArrays, 0);
+                              &dynamicStateArrays, 0, GrPrimitiveType::kTriangleStrip);
 
     flushState->opsRenderPass()->draw(programInfo,
                                       fMeshesBuffer.begin(), fMeshesBuffer.count(),
diff --git a/src/gpu/ccpr/GrCCStroker.h b/src/gpu/ccpr/GrCCStroker.h
index 985ac38..37e6840 100644
--- a/src/gpu/ccpr/GrCCStroker.h
+++ b/src/gpu/ccpr/GrCCStroker.h
@@ -97,8 +97,8 @@
     void appendStrokeMeshesToBuffers(int numSegmentsLog2, const Batch&,
                                      const InstanceTallies* startIndices[2],
                                      int startScissorSubBatch, const SkIRect& drawBounds) const;
-    void flushBufferedMeshesAsStrokes(const GrPrimitiveProcessor&, GrOpFlushState*, const
-                                      GrPipeline&, const SkIRect& drawBounds) const;
+    void flushBufferedMeshesAsStrokes(const GrPrimitiveProcessor&, GrOpFlushState*,
+                                      const GrPipeline&, const SkIRect& drawBounds) const;
 
     template<int GrCCStrokeGeometry::InstanceTallies::* InstanceType>
     void drawConnectingGeometry(GrOpFlushState*, const GrPipeline&,
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index 4e86d7e..a1c044e 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -43,7 +43,7 @@
     if (!caps.driverBlacklistMSAACCPR() &&
         caps.internalMultisampleCount(defaultA8Format) > 1 &&
         caps.sampleLocationsSupport() &&
-        shaderCaps.sampleVariablesStencilSupport()) {
+        shaderCaps.sampleMaskSupport()) {
         if (coverageType) {
             *coverageType = CoverageType::kA8_Multisample;
         }
diff --git a/src/gpu/ccpr/GrGSCoverageProcessor.cpp b/src/gpu/ccpr/GrGSCoverageProcessor.cpp
index 1398d5b..09941fd 100644
--- a/src/gpu/ccpr/GrGSCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrGSCoverageProcessor.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/ccpr/GrGSCoverageProcessor.h"
 
 #include "src/gpu/GrMesh.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
 using InputType = GrGLSLGeometryBuilder::InputType;
@@ -23,8 +24,8 @@
     virtual bool hasCoverage(const GrGSCoverageProcessor& proc) const { return false; }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) final {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                 const CoordTransformRange& transformRange) final {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
     }
 
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
@@ -39,7 +40,7 @@
         this->emitGeometryShader(proc, varyingHandler, args.fGeomBuilder, args.fRTAdjustName);
         varyingHandler->emitAttributes(proc);
         varyingHandler->setNoPerspective();
-        SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+        SkASSERT(!*args.fFPCoordTransformHandler);
 
         // Fragment shader.
         GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
@@ -446,8 +447,7 @@
     // and does edge AA. The second pass does touch up on corner pixels.
     for (int i = 0; i < 2; ++i) {
         fSubpass = (Subpass) i;
-        this->GrCCCoverageProcessor::draw(
-                flushState, pipeline, scissorRects, meshes, meshCount, drawBounds);
+        INHERITED::draw(flushState, pipeline, scissorRects, meshes, meshCount, drawBounds);
     }
 }
 
diff --git a/src/gpu/ccpr/GrGSCoverageProcessor.h b/src/gpu/ccpr/GrGSCoverageProcessor.h
index 2e3f7dc..48df1b5 100644
--- a/src/gpu/ccpr/GrGSCoverageProcessor.h
+++ b/src/gpu/ccpr/GrGSCoverageProcessor.h
@@ -33,6 +33,8 @@
     void draw(GrOpFlushState*, const GrPipeline&, const SkIRect scissorRects[], const GrMesh[],
               int meshCount, const SkRect& drawBounds) const override;
 
+    GrPrimitiveType primType() const final { return GrPrimitiveType::kLines; }
+
     GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const override;
 
     // The geometry shader impl draws primitives in two subpasses. The first pass fills the interior
@@ -49,6 +51,8 @@
     class TriangleHullImpl;
     class CurveHullImpl;
     class CornerImpl;
+
+    typedef GrCCCoverageProcessor INHERITED;
 };
 
 #endif
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.cpp b/src/gpu/ccpr/GrSampleMaskProcessor.cpp
index 41da1f9..d40fd77 100644
--- a/src/gpu/ccpr/GrSampleMaskProcessor.cpp
+++ b/src/gpu/ccpr/GrSampleMaskProcessor.cpp
@@ -16,7 +16,7 @@
 
 private:
     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&&) override {}
+                 const CoordTransformRange&) override {}
 
     void onEmitCode(EmitArgs&, GrGPArgs*) override;
 
@@ -31,7 +31,7 @@
     int inputWidth = (4 == numInputPoints || proc.hasInputWeight()) ? 4 : 3;
 
     varyingHandler->emitAttributes(proc);
-    SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+    SkASSERT(!*args.fFPCoordTransformHandler);
 
     if (PrimitiveType::kTriangles == proc.fPrimitiveType) {
         SkASSERT(!proc.hasInstanceAttributes());  // Triangles are drawn with vertex arrays.
@@ -112,6 +112,22 @@
     }
 }
 
+GrPrimitiveType GrSampleMaskProcessor::primType() const {
+    SkASSERT(PrimitiveType::kWeightedTriangles != fPrimitiveType);
+
+    switch (fPrimitiveType) {
+        case PrimitiveType::kTriangles:
+        case PrimitiveType::kWeightedTriangles:
+            return GrPrimitiveType::kTriangles;
+        case PrimitiveType::kQuadratics:
+        case PrimitiveType::kCubics:
+        case PrimitiveType::kConics:
+            return GrPrimitiveType::kTriangleStrip;
+        default:
+            return GrPrimitiveType::kTriangleStrip;
+    }
+}
+
 GrGLSLPrimitiveProcessor* GrSampleMaskProcessor::onCreateGLSLInstance(
         std::unique_ptr<Shader> shader) const {
     return new Impl(std::move(shader));
diff --git a/src/gpu/ccpr/GrSampleMaskProcessor.h b/src/gpu/ccpr/GrSampleMaskProcessor.h
index 3fdd96f..daf9bed 100644
--- a/src/gpu/ccpr/GrSampleMaskProcessor.h
+++ b/src/gpu/ccpr/GrSampleMaskProcessor.h
@@ -23,6 +23,8 @@
     void appendMesh(sk_sp<const GrGpuBuffer> instanceBuffer, int instanceCount, int baseInstance,
                     SkTArray<GrMesh>* out) const override;
 
+    GrPrimitiveType primType() const final;
+
     GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const override;
 
     SkSTArray<2, Attribute> fInputAttribs;
diff --git a/src/gpu/ccpr/GrStencilAtlasOp.cpp b/src/gpu/ccpr/GrStencilAtlasOp.cpp
index c7ec6da..2fed59e 100644
--- a/src/gpu/ccpr/GrStencilAtlasOp.cpp
+++ b/src/gpu/ccpr/GrStencilAtlasOp.cpp
@@ -21,7 +21,7 @@
 
 class StencilResolveProcessor : public GrGeometryProcessor {
 public:
-    StencilResolveProcessor() : GrGeometryProcessor(kStencilResolveProcessor_ClassID) {
+    StencilResolveProcessor() : INHERITED(kStencilResolveProcessor_ClassID) {
         static constexpr Attribute kIBounds = {
                 "ibounds", kShort4_GrVertexAttribType, kShort4_GrSLType};
         this->setInstanceAttributes(&kIBounds, 1);
@@ -29,10 +29,12 @@
     }
 
 private:
-    const char* name() const override { return "GrCCPathProcessor"; }
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
+    const char* name() const final { return "StencilResolveProcessor"; }
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
     class Impl;
+
+    typedef GrGeometryProcessor INHERITED;
 };
 
 // This processor draws pixel-aligned rectangles directly on top of every path in the atlas.
@@ -57,7 +59,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&&) override {}
+                 const CoordTransformRange&) override {}
 };
 
 GrGLSLPrimitiveProcessor* StencilResolveProcessor::createGLSLInstance(const GrShaderCaps&) const {
@@ -90,11 +92,13 @@
 );
 
 // Resolves stencil winding counts to A8 coverage. Leaves stencil values untouched.
+// NOTE: For the CCW face we intentionally use "1 == (stencil & 1)" because the contrapositive logic
+// (i.e. 0 != ...) causes bugs on Adreno Vulkan. http://skbug.com/9643
 static constexpr GrUserStencilSettings kResolveStencilCoverage(
     GrUserStencilSettings::StaticInitSeparate<
-        0x0000,                           0x0000,
-        GrUserStencilTest::kNotEqual,     GrUserStencilTest::kNotEqual,
-        0xffff,                           0x1,
+        0x0000,                           0x0001,
+        GrUserStencilTest::kNotEqual,     GrUserStencilTest::kEqual,
+        0xffff,                           0x0001,
         GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
         GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
         0xffff,                           0xffff>()
@@ -106,7 +110,7 @@
     GrUserStencilSettings::StaticInitSeparate<
         0x0000,                           0x0000,
         GrUserStencilTest::kNotEqual,     GrUserStencilTest::kNotEqual,
-        0xffff,                           0x1,
+        0xffff,                           0x0001,
         GrUserStencilOp::kZero,           GrUserStencilOp::kZero,
         GrUserStencilOp::kKeep,           GrUserStencilOp::kKeep,
         0xffff,                           0xffff>()
@@ -151,12 +155,14 @@
 
     StencilResolveProcessor primProc;
 
-    GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                              flushState->drawOpArgs().origin(),
-                              resolvePipeline,
-                              primProc,
+    GrProgramInfo programInfo(flushState->proxy()->numSamples(),
+                              flushState->proxy()->numStencilSamples(),
+                              flushState->proxy()->backendFormat(),
+                              flushState->view()->origin(),
+                              &resolvePipeline,
+                              &primProc,
                               &scissorRectState,
-                              nullptr, 0);
+                              nullptr, 0, GrPrimitiveType::kTriangleStrip);
 
     flushState->opsRenderPass()->draw(programInfo, &mesh, 1, SkRect::Make(drawBoundsRect));
 }
diff --git a/src/gpu/ccpr/GrVSCoverageProcessor.cpp b/src/gpu/ccpr/GrVSCoverageProcessor.cpp
index ae5cfca..05819f3 100644
--- a/src/gpu/ccpr/GrVSCoverageProcessor.cpp
+++ b/src/gpu/ccpr/GrVSCoverageProcessor.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/ccpr/GrVSCoverageProcessor.h"
 
 #include "src/gpu/GrMesh.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
 // This class implements the coverage processor with vertex shaders.
@@ -18,8 +19,8 @@
 
 private:
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) final {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                 const CoordTransformRange& transformRange) final {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
     }
 
     void onEmitCode(EmitArgs&, GrGPArgs*) override;
@@ -441,7 +442,7 @@
                           "vertexpos", "coverage", "corner_coverage", "wind");
 
     varyingHandler->emitAttributes(proc);
-    SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
+    SkASSERT(!*args.fFPCoordTransformHandler);
 
     // Fragment shader.
     GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
@@ -531,6 +532,9 @@
 
 void GrVSCoverageProcessor::appendMesh(sk_sp<const GrGpuBuffer> instanceBuffer, int instanceCount,
                                        int baseInstance, SkTArray<GrMesh>* out) const {
+    SkASSERT(fTriangleType == GrPrimitiveType::kTriangles ||
+             fTriangleType == GrPrimitiveType::kTriangleStrip);
+
     GrMesh& mesh = out->emplace_back(fTriangleType);
     auto primitiveRestart = GrPrimitiveRestart(GrPrimitiveType::kTriangleStrip == fTriangleType);
     mesh.setIndexedInstanced(fIndexBuffer, fNumIndicesPerInstance, std::move(instanceBuffer),
diff --git a/src/gpu/ccpr/GrVSCoverageProcessor.h b/src/gpu/ccpr/GrVSCoverageProcessor.h
index 6f4ce45..ea2372f 100644
--- a/src/gpu/ccpr/GrVSCoverageProcessor.h
+++ b/src/gpu/ccpr/GrVSCoverageProcessor.h
@@ -23,6 +23,8 @@
     void appendMesh(sk_sp<const GrGpuBuffer> instanceBuffer, int instanceCount, int baseInstance,
                     SkTArray<GrMesh>* out) const override;
 
+    GrPrimitiveType primType() const final { return fTriangleType; }
+
     GrGLSLPrimitiveProcessor* onCreateGLSLInstance(std::unique_ptr<Shader>) const override;
 
     Attribute fPerVertexData;
@@ -30,7 +32,7 @@
     sk_sp<const GrGpuBuffer> fVertexBuffer;
     sk_sp<const GrGpuBuffer> fIndexBuffer;
     int fNumIndicesPerInstance;
-    GrPrimitiveType fTriangleType;
+    GrPrimitiveType fTriangleType = GrPrimitiveType::kPoints;
 
     class Impl;
 };
diff --git a/src/gpu/dawn/GrDawnBuffer.cpp b/src/gpu/dawn/GrDawnBuffer.cpp
index b01cbcd..6af4133 100644
--- a/src/gpu/dawn/GrDawnBuffer.cpp
+++ b/src/gpu/dawn/GrDawnBuffer.cpp
@@ -10,19 +10,19 @@
 #include "src/gpu/dawn/GrDawnGpu.h"
 
 namespace {
-    dawn::BufferUsage GrGpuBufferTypeToDawnUsageBit(GrGpuBufferType type) {
+    wgpu::BufferUsage GrGpuBufferTypeToDawnUsageBit(GrGpuBufferType type) {
         switch (type) {
             case GrGpuBufferType::kVertex:
-                return dawn::BufferUsage::Vertex;
+                return wgpu::BufferUsage::Vertex;
             case GrGpuBufferType::kIndex:
-                return dawn::BufferUsage::Index;
+                return wgpu::BufferUsage::Index;
             case GrGpuBufferType::kXferCpuToGpu:
-                return dawn::BufferUsage::CopySrc;
+                return wgpu::BufferUsage::CopySrc;
             case GrGpuBufferType::kXferGpuToCpu:
-                return dawn::BufferUsage::CopyDst;
+                return wgpu::BufferUsage::CopyDst;
             default:
                 SkASSERT(!"buffer type not supported by Dawn");
-                return dawn::BufferUsage::Vertex;
+                return wgpu::BufferUsage::Vertex;
         }
     }
 }
@@ -31,9 +31,9 @@
                            GrAccessPattern pattern)
     : INHERITED(gpu, sizeInBytes, type, pattern)
     , fStagingBuffer(nullptr) {
-    dawn::BufferDescriptor bufferDesc;
+    wgpu::BufferDescriptor bufferDesc;
     bufferDesc.size = sizeInBytes;
-    bufferDesc.usage = GrGpuBufferTypeToDawnUsageBit(type) | dawn::BufferUsage::CopyDst;
+    bufferDesc.usage = GrGpuBufferTypeToDawnUsageBit(type) | wgpu::BufferUsage::CopyDst;
     fBuffer = this->getDawnGpu()->device().CreateBuffer(&bufferDesc);
     this->registerWithCache(SkBudgeted::kYes);
 }
diff --git a/src/gpu/dawn/GrDawnBuffer.h b/src/gpu/dawn/GrDawnBuffer.h
index 17fa1c8..1d762e5 100644
--- a/src/gpu/dawn/GrDawnBuffer.h
+++ b/src/gpu/dawn/GrDawnBuffer.h
@@ -24,10 +24,10 @@
     bool onUpdateData(const void* src, size_t srcSizeInBytes) override;
 
     GrDawnGpu* getDawnGpu() const;
-    dawn::Buffer get() const { return fBuffer; }
+    wgpu::Buffer get() const { return fBuffer; }
 
 private:
-    dawn::Buffer fBuffer;
+    wgpu::Buffer fBuffer;
     GrDawnStagingBuffer* fStagingBuffer;
     typedef GrGpuBuffer INHERITED;
 };
diff --git a/src/gpu/dawn/GrDawnCaps.cpp b/src/gpu/dawn/GrDawnCaps.cpp
index 8caa134..52ff461 100644
--- a/src/gpu/dawn/GrDawnCaps.cpp
+++ b/src/gpu/dawn/GrDawnCaps.cpp
@@ -7,6 +7,12 @@
 
 #include "src/gpu/dawn/GrDawnCaps.h"
 
+#include "src/gpu/GrProgramDesc.h"
+#include "src/gpu/GrProgramInfo.h"
+#include "src/gpu/GrRenderTarget.h"
+#include "src/gpu/GrRenderTargetPriv.h"
+#include "src/gpu/GrStencilSettings.h"
+
 GrDawnCaps::GrDawnCaps(const GrContextOptions& contextOptions) : INHERITED(contextOptions) {
     fMipMapSupport = true;
     fBufferMapThreshold = SK_MaxS32;  // FIXME: get this from Dawn?
@@ -15,6 +21,7 @@
     fMaxVertexAttributes = 16; // FIXME
     fClampToBorderSupport = false;
     fPerformPartialClearsAsDraws = true;
+    fDynamicStateArrayGeometryProcessorTextureSupport = true;
 
     fShaderCaps->fFlatInterpolationSupport = true;
     fShaderCaps->fIntegerSupport = true;
@@ -24,8 +31,7 @@
     fShaderCaps->fMaxFragmentSamplers = 6;
     fShaderCaps->fShaderDerivativeSupport = true;
 
-    this->applyOptionsOverrides(contextOptions);
-    fShaderCaps->applyOptionsOverrides(contextOptions);
+    this->finishInitialization(contextOptions);
 }
 
 bool GrDawnCaps::isFormatSRGB(const GrBackendFormat& format) const {
@@ -39,13 +45,13 @@
 
 bool GrDawnCaps::isFormatTexturable(const GrBackendFormat& format) const {
     // Currently, all the formats in GrDawnFormatToPixelConfig are texturable.
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     return format.asDawnFormat(&dawnFormat);
 }
 
 GrPixelConfig GrDawnCaps::onGetConfigFromBackendFormat(const GrBackendFormat& format,
                                                        GrColorType colorType) const {
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     if (!format.asDawnFormat(&dawnFormat)) {
         return kUnknown_GrPixelConfig;
     }
@@ -53,14 +59,14 @@
         case GrColorType::kUnknown:
             return kUnknown_GrPixelConfig;
         case GrColorType::kAlpha_8:
-            if (dawn::TextureFormat::R8Unorm == dawnFormat) {
+            if (wgpu::TextureFormat::R8Unorm == dawnFormat) {
                 return kAlpha_8_as_Red_GrPixelConfig;
             }
             break;
         case GrColorType::kRGBA_8888:
-            if (dawn::TextureFormat::RGBA8Unorm == dawnFormat) {
+            if (wgpu::TextureFormat::RGBA8Unorm == dawnFormat) {
                 return kRGBA_8888_GrPixelConfig;
-            } else if (dawn::TextureFormat::BGRA8Unorm == dawnFormat) {
+            } else if (wgpu::TextureFormat::BGRA8Unorm == dawnFormat) {
                 // FIXME: This shouldn't be necessary, but on some platforms (Mac)
                 // Skia byte order is RGBA, while preferred swap format is BGRA.
                 return kBGRA_8888_GrPixelConfig;
@@ -69,9 +75,9 @@
         case GrColorType::kRGB_888x:
             break;
         case GrColorType::kBGRA_8888:
-            if (dawn::TextureFormat::BGRA8Unorm == dawnFormat) {
+            if (wgpu::TextureFormat::BGRA8Unorm == dawnFormat) {
                 return kBGRA_8888_GrPixelConfig;
-            } else if (dawn::TextureFormat::RGBA8Unorm == dawnFormat) {
+            } else if (wgpu::TextureFormat::RGBA8Unorm == dawnFormat) {
                 return kRGBA_8888_GrPixelConfig;
             }
             break;
@@ -108,18 +114,18 @@
 
 bool GrDawnCaps::isFormatTexturableAndUploadable(GrColorType ct,
                                                  const GrBackendFormat& format) const {
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     if (!format.asDawnFormat(&dawnFormat)) {
         return false;
     }
     switch (ct) {
         case GrColorType::kAlpha_8:
-            return dawn::TextureFormat::R8Unorm == dawnFormat;
+            return wgpu::TextureFormat::R8Unorm == dawnFormat;
         case GrColorType::kRGBA_8888:
         case GrColorType::kRGB_888x:
         case GrColorType::kBGRA_8888:
-            return dawn::TextureFormat::RGBA8Unorm == dawnFormat ||
-                   dawn::TextureFormat::BGRA8Unorm == dawnFormat;
+            return wgpu::TextureFormat::RGBA8Unorm == dawnFormat ||
+                   wgpu::TextureFormat::BGRA8Unorm == dawnFormat;
         default:
             return false;
     }
@@ -127,7 +133,7 @@
 
 bool GrDawnCaps::isFormatRenderable(const GrBackendFormat& format,
                                     int sampleCount) const {
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     if (!format.isValid() || sampleCount > 1 || !format.asDawnFormat(&dawnFormat)) {
         return false;
     }
@@ -141,7 +147,7 @@
 }
 
 size_t GrDawnCaps::bytesPerPixel(const GrBackendFormat& backendFormat) const {
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     if (!backendFormat.asDawnFormat(&dawnFormat)) {
         return 0;
     }
@@ -150,7 +156,7 @@
 
 int GrDawnCaps::getRenderTargetSampleCount(int requestedCount,
                                            const GrBackendFormat& backendFormat) const {
-    dawn::TextureFormat dawnFormat;
+    wgpu::TextureFormat dawnFormat;
     if (!backendFormat.asDawnFormat(&dawnFormat)) {
         return 0;
     }
@@ -167,7 +173,7 @@
     if (config == kUnknown_GrPixelConfig) {
         return GrBackendFormat();
     }
-    dawn::TextureFormat format;
+    wgpu::TextureFormat format;
     if (!GrPixelConfigToDawnFormat(config, &format)) {
         return GrBackendFormat();
     }
@@ -196,29 +202,76 @@
 
 GrColorType GrDawnCaps::getYUVAColorTypeFromBackendFormat(const GrBackendFormat& backendFormat,
                                                           bool isAlphaChannel) const {
-    dawn::TextureFormat textureFormat;
+    wgpu::TextureFormat textureFormat;
     if (!backendFormat.asDawnFormat(&textureFormat)) {
         return GrColorType::kUnknown;
     }
     switch (textureFormat) {
-        case dawn::TextureFormat::R8Unorm:     return isAlphaChannel ? GrColorType::kAlpha_8
+        case wgpu::TextureFormat::R8Unorm:     return isAlphaChannel ? GrColorType::kAlpha_8
                                                                      : GrColorType::kGray_8;
-        case dawn::TextureFormat::RGBA8Unorm:  return GrColorType::kRGBA_8888;
-        case dawn::TextureFormat::BGRA8Unorm:  return GrColorType::kBGRA_8888;
+        case wgpu::TextureFormat::RGBA8Unorm:  return GrColorType::kRGBA_8888;
+        case wgpu::TextureFormat::BGRA8Unorm:  return GrColorType::kBGRA_8888;
         default:                               return GrColorType::kUnknown;
     }
 }
 
+// FIXME: taken from GrVkPipelineState; refactor.
+static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
+    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
+
+    static const uint32_t kBlendWriteShift = 1;
+    static const uint32_t kBlendCoeffShift = 5;
+    GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
+    GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
+
+    uint32_t key = blendInfo.fWriteColor;
+    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
+    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
+    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
+
+    return key;
+}
+
+GrProgramDesc GrDawnCaps::makeDesc(const GrRenderTarget* rt,
+                                   const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    wgpu::TextureFormat format;
+    if (!programInfo.backendFormat().asDawnFormat(&format)) {
+        desc.key().reset();
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
+    stencil.genKey(&b);
+
+    // TODO: remove this reliance on the renderTarget
+    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
+
+    b.add32(static_cast<uint32_t>(format));
+    b.add32(static_cast<int32_t>(hasDepthStencil));
+    b.add32(get_blend_info_key(programInfo.pipeline()));
+    b.add32(static_cast<uint32_t>(programInfo.primitiveType()));
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrDawnCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
-        { GrColorType::kAlpha_8,   GrBackendFormat::MakeDawn(dawn::TextureFormat::R8Unorm)    },
-        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(dawn::TextureFormat::RGBA8Unorm) },
-        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(dawn::TextureFormat::BGRA8Unorm) },
-        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(dawn::TextureFormat::RGBA8Unorm) },
-        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(dawn::TextureFormat::BGRA8Unorm) },
-        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(dawn::TextureFormat::BGRA8Unorm) },
-        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(dawn::TextureFormat::RGBA8Unorm) },
+        { GrColorType::kAlpha_8,   GrBackendFormat::MakeDawn(wgpu::TextureFormat::R8Unorm)    },
+        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
+        { GrColorType::kRGBA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
+        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
+        { GrColorType::kRGB_888x,  GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
+        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::BGRA8Unorm) },
+        { GrColorType::kBGRA_8888, GrBackendFormat::MakeDawn(wgpu::TextureFormat::RGBA8Unorm) },
     };
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/dawn/GrDawnCaps.h b/src/gpu/dawn/GrDawnCaps.h
index 6af5d7d..7ae3cb1 100644
--- a/src/gpu/dawn/GrDawnCaps.h
+++ b/src/gpu/dawn/GrDawnCaps.h
@@ -58,6 +58,8 @@
     GrColorType getYUVAColorTypeFromBackendFormat(const GrBackendFormat&,
                                                   bool isAlphaChannel) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/dawn/GrDawnGpu.cpp b/src/gpu/dawn/GrDawnGpu.cpp
index 186c1e6..18aa4eb 100644
--- a/src/gpu/dawn/GrDawnGpu.cpp
+++ b/src/gpu/dawn/GrDawnGpu.cpp
@@ -37,78 +37,36 @@
 
 const int kMaxRenderPipelineEntries = 1024;
 
-static dawn::FilterMode to_dawn_filter_mode(GrSamplerState::Filter filter) {
+static wgpu::FilterMode to_dawn_filter_mode(GrSamplerState::Filter filter) {
     switch (filter) {
         case GrSamplerState::Filter::kNearest:
-            return dawn::FilterMode::Nearest;
+            return wgpu::FilterMode::Nearest;
         case GrSamplerState::Filter::kBilerp:
         case GrSamplerState::Filter::kMipMap:
-            return dawn::FilterMode::Linear;
+            return wgpu::FilterMode::Linear;
         default:
             SkASSERT(!"unsupported filter mode");
-            return dawn::FilterMode::Nearest;
+            return wgpu::FilterMode::Nearest;
     }
 }
 
-static dawn::AddressMode to_dawn_address_mode(GrSamplerState::WrapMode wrapMode) {
+static wgpu::AddressMode to_dawn_address_mode(GrSamplerState::WrapMode wrapMode) {
     switch (wrapMode) {
         case GrSamplerState::WrapMode::kClamp:
-            return dawn::AddressMode::ClampToEdge;
+            return wgpu::AddressMode::ClampToEdge;
         case GrSamplerState::WrapMode::kRepeat:
-            return dawn::AddressMode::Repeat;
+            return wgpu::AddressMode::Repeat;
         case GrSamplerState::WrapMode::kMirrorRepeat:
-            return dawn::AddressMode::MirrorRepeat;
+            return wgpu::AddressMode::MirrorRepeat;
         case GrSamplerState::WrapMode::kClampToBorder:
             SkASSERT(!"unsupported address mode");
     }
     SkASSERT(!"unsupported address mode");
-    return dawn::AddressMode::ClampToEdge;
+    return wgpu::AddressMode::ClampToEdge;
 
 }
 
-// FIXME: taken from GrVkPipelineState; refactor.
-static uint32_t get_blend_info_key(const GrPipeline& pipeline) {
-    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
-
-    static const uint32_t kBlendWriteShift = 1;
-    static const uint32_t kBlendCoeffShift = 5;
-    GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
-    GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
-
-    uint32_t key = blendInfo.fWriteColor;
-    key |= (blendInfo.fSrcBlend << kBlendWriteShift);
-    key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
-    key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
-
-    return key;
-}
-
-class Desc : public GrProgramDesc {
-public:
-    static bool Build(Desc* desc,
-                      GrRenderTarget* rt,
-                      const GrProgramInfo& programInfo,
-                      GrPrimitiveType primitiveType,
-                      bool hasDepthStencil,
-                      GrGpu* gpu) {
-        if (!GrProgramDesc::Build(desc, rt, programInfo, primitiveType, gpu)) {
-            return false;
-        }
-        GrProcessorKeyBuilder b(&desc->key());
-
-        GrStencilSettings stencil;
-        const GrPipeline& pipeline = programInfo.pipeline();
-        stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(), 8);
-        stencil.genKey(&b);
-        b.add32(rt->config());
-        b.add32(static_cast<int32_t>(hasDepthStencil));
-        b.add32(get_blend_info_key(pipeline));
-        b.add32(static_cast<uint32_t>(primitiveType));
-        return true;
-    }
-};
-
-sk_sp<GrGpu> GrDawnGpu::Make(const dawn::Device& device,
+sk_sp<GrGpu> GrDawnGpu::Make(const wgpu::Device& device,
                              const GrContextOptions& options, GrContext* context) {
     if (!device) {
         return nullptr;
@@ -120,12 +78,12 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 GrDawnGpu::GrDawnGpu(GrContext* context, const GrContextOptions& options,
-                     const dawn::Device& device)
+                     const wgpu::Device& device)
         : INHERITED(context)
         , fDevice(device)
         , fQueue(device.CreateQueue())
         , fCompiler(new SkSL::Compiler())
-        , fUniformRingBuffer(this, dawn::BufferUsage::Uniform)
+        , fUniformRingBuffer(this, wgpu::BufferUsage::Uniform)
         , fRenderPipelineCache(kMaxRenderPipelineEntries)
         , fStagingManager(fDevice) {
     fCaps.reset(new GrDawnCaps(options));
@@ -145,7 +103,7 @@
             GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
             const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     fOpsRenderPass.reset(new GrDawnOpsRenderPass(this, rt, origin, colorInfo, stencilInfo));
     return fOpsRenderPass.get();
 }
@@ -199,7 +157,7 @@
                                             int mipLevelCount,
                                             uint32_t levelClearMask) {
     SkASSERT(!levelClearMask);
-    dawn::TextureFormat format;
+    wgpu::TextureFormat format;
     if (!backendFormat.asDawnFormat(&format)) {
         return nullptr;
     }
@@ -305,63 +263,53 @@
     return stencil;
 }
 
-GrBackendTexture GrDawnGpu::onCreateBackendTexture(int width, int height,
+GrBackendTexture GrDawnGpu::onCreateBackendTexture(SkISize dimensions,
                                                    const GrBackendFormat& backendFormat,
-                                                   GrMipMapped mipMapped,
                                                    GrRenderable renderable,
-                                                   const SkPixmap srcData[],
+                                                   const BackendTextureData* data,
                                                    int numMipLevels,
-                                                   const SkColor4f* color,
                                                    GrProtected isProtected) {
-    dawn::TextureFormat format;
+    wgpu::TextureFormat format;
     if (!backendFormat.asDawnFormat(&format)) {
         return GrBackendTexture();
     }
 
-    SkASSERT(width <= this->caps()->maxTextureSize() && height <= this->caps()->maxTextureSize());
-
     // FIXME: Dawn doesn't support mipmapped render targets (yet).
-    if (GrMipMapped::kYes == mipMapped && GrRenderable::kYes == renderable) {
+    if (numMipLevels > 1 && GrRenderable::kYes == renderable) {
         return GrBackendTexture();
     }
 
-    dawn::TextureDescriptor desc;
+    wgpu::TextureDescriptor desc;
     desc.usage =
-        dawn::TextureUsage::Sampled |
-        dawn::TextureUsage::CopySrc |
-        dawn::TextureUsage::CopyDst;
+        wgpu::TextureUsage::Sampled |
+        wgpu::TextureUsage::CopySrc |
+        wgpu::TextureUsage::CopyDst;
 
     if (GrRenderable::kYes == renderable) {
-        desc.usage |= dawn::TextureUsage::OutputAttachment;
+        desc.usage |= wgpu::TextureUsage::OutputAttachment;
     }
 
-    desc.size.width = width;
-    desc.size.height = height;
+    desc.size.width = dimensions.width();
+    desc.size.height = dimensions.height();
     desc.size.depth = 1;
     desc.format = format;
+    desc.mipLevelCount = numMipLevels;
 
-    // Figure out the number of mip levels.
-    if (srcData) {
-        desc.mipLevelCount = numMipLevels;
-    } else if (GrMipMapped::kYes == mipMapped) {
-        desc.mipLevelCount = SkMipMap::ComputeLevelCount(width, height) + 1;
-    }
-
-    dawn::Texture tex = this->device().CreateTexture(&desc);
+    wgpu::Texture tex = this->device().CreateTexture(&desc);
 
     size_t bpp = GrDawnBytesPerPixel(format);
-    size_t baseLayerSize = bpp * width * height;
+    size_t baseLayerSize = bpp * dimensions.width() * dimensions.height();
     const void* pixels;
     SkAutoMalloc defaultStorage(baseLayerSize);
-    if (srcData) {
-        pixels = srcData->addr();
+    if (data && data->type() == BackendTextureData::Type::kPixmaps) {
+        pixels = data->pixmap(0).addr();
     } else {
         pixels = defaultStorage.get();
         memset(defaultStorage.get(), 0, baseLayerSize);
     }
-    dawn::Device device = this->device();
-    dawn::CommandEncoder copyEncoder = fDevice.CreateCommandEncoder();
-    int w = width, h = height;
+    wgpu::Device device = this->device();
+    wgpu::CommandEncoder copyEncoder = fDevice.CreateCommandEncoder();
+    int w = dimensions.width(), h = dimensions.height();
     for (uint32_t i = 0; i < desc.mipLevelCount; i++) {
         size_t origRowBytes = bpp * w;
         size_t rowBytes = GrDawnRoundRowBytes(origRowBytes);
@@ -378,30 +326,30 @@
                 src += origRowBytes;
             }
         }
-        dawn::Buffer buffer = stagingBuffer->fBuffer;
+        wgpu::Buffer buffer = stagingBuffer->fBuffer;
         buffer.Unmap();
         stagingBuffer->fData = nullptr;
-        dawn::BufferCopyView srcBuffer;
+        wgpu::BufferCopyView srcBuffer;
         srcBuffer.buffer = buffer;
         srcBuffer.offset = 0;
         srcBuffer.rowPitch = rowBytes;
         srcBuffer.imageHeight = h;
-        dawn::TextureCopyView dstTexture;
+        wgpu::TextureCopyView dstTexture;
         dstTexture.texture = tex;
         dstTexture.mipLevel = i;
         dstTexture.origin = {0, 0, 0};
-        dawn::Extent3D copySize = {(uint32_t) w, (uint32_t) h, 1};
+        wgpu::Extent3D copySize = {(uint32_t) w, (uint32_t) h, 1};
         copyEncoder.CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
         w = SkTMax(1, w / 2);
         h = SkTMax(1, h / 2);
     }
-    dawn::CommandBuffer cmdBuf = copyEncoder.Finish();
+    wgpu::CommandBuffer cmdBuf = copyEncoder.Finish();
     fQueue.Submit(1, &cmdBuf);
     GrDawnImageInfo info;
     info.fTexture = tex;
     info.fFormat = desc.format;
     info.fLevelCount = desc.mipLevelCount;
-    return GrBackendTexture(width, height, info);
+    return GrBackendTexture(dimensions.width(), dimensions.height(), info);
 }
 
 void GrDawnGpu::deleteBackendTexture(const GrBackendTexture& tex) {
@@ -429,22 +377,22 @@
         return GrBackendRenderTarget();
     }
 
-    dawn::TextureFormat format;
+    wgpu::TextureFormat format;
     if (!GrPixelConfigToDawnFormat(config, &format)) {
         return GrBackendRenderTarget();
     }
 
-    dawn::TextureDescriptor desc;
+    wgpu::TextureDescriptor desc;
     desc.usage =
-        dawn::TextureUsage::CopySrc |
-        dawn::TextureUsage::OutputAttachment;
+        wgpu::TextureUsage::CopySrc |
+        wgpu::TextureUsage::OutputAttachment;
 
     desc.size.width = width;
     desc.size.height = height;
     desc.size.depth = 1;
     desc.format = format;
 
-    dawn::Texture tex = this->device().CreateTexture(&desc);
+    wgpu::Texture tex = this->device().CreateTexture(&desc);
 
     GrDawnImageInfo info;
     info.fTexture = tex;
@@ -476,12 +424,13 @@
     fDevice.Tick();
 }
 
-void GrDawnGpu::onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+bool GrDawnGpu::onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                               const GrFlushInfo& info, const GrPrepareForExternalIORequests&) {
     this->flush();
+    return true;
 }
 
-static dawn::Texture get_dawn_texture_from_surface(GrSurface* src) {
+static wgpu::Texture get_dawn_texture_from_surface(GrSurface* src) {
     if (auto rt = static_cast<GrDawnRenderTarget*>(src->asRenderTarget())) {
         return rt->texture();
     } else if (auto t = static_cast<GrDawnTexture*>(src->asTexture())) {
@@ -495,21 +444,21 @@
                               GrSurface* src,
                               const SkIRect& srcRect,
                               const SkIPoint& dstPoint) {
-    dawn::Texture srcTexture = get_dawn_texture_from_surface(src);
-    dawn::Texture dstTexture = get_dawn_texture_from_surface(dst);
+    wgpu::Texture srcTexture = get_dawn_texture_from_surface(src);
+    wgpu::Texture dstTexture = get_dawn_texture_from_surface(dst);
     if (!srcTexture || !dstTexture) {
         return false;
     }
 
     uint32_t width = srcRect.width(), height = srcRect.height();
 
-    dawn::TextureCopyView srcTextureView, dstTextureView;
+    wgpu::TextureCopyView srcTextureView, dstTextureView;
     srcTextureView.texture = srcTexture;
     srcTextureView.origin = {(uint32_t) srcRect.x(), (uint32_t) srcRect.y(), 0};
     dstTextureView.texture = dstTexture;
     dstTextureView.origin = {(uint32_t) dstPoint.x(), (uint32_t) dstPoint.y(), 0};
 
-    dawn::Extent3D copySize = {width, height, 1};
+    wgpu::Extent3D copySize = {width, height, 1};
     this->getCopyEncoder().CopyTextureToTexture(&srcTextureView, &dstTextureView, &copySize);
     return true;
 }
@@ -522,7 +471,7 @@
 bool GrDawnGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
                              GrColorType surfaceColorType, GrColorType dstColorType, void* buffer,
                              size_t rowBytes) {
-    dawn::Texture tex = get_dawn_texture_from_surface(surface);
+    wgpu::Texture tex = get_dawn_texture_from_surface(surface);
 
     if (0 == rowBytes) {
         return false;
@@ -532,23 +481,23 @@
     rowBytes = GrDawnRoundRowBytes(rowBytes);
     int sizeInBytes = rowBytes * height;
 
-    dawn::BufferDescriptor desc;
-    desc.usage = dawn::BufferUsage::CopyDst | dawn::BufferUsage::MapRead;
+    wgpu::BufferDescriptor desc;
+    desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
     desc.size = sizeInBytes;
 
-    dawn::Buffer buf = device().CreateBuffer(&desc);
+    wgpu::Buffer buf = device().CreateBuffer(&desc);
 
-    dawn::TextureCopyView srcTexture;
+    wgpu::TextureCopyView srcTexture;
     srcTexture.texture = tex;
     srcTexture.origin = {(uint32_t) left, (uint32_t) top, 0};
 
-    dawn::BufferCopyView dstBuffer;
+    wgpu::BufferCopyView dstBuffer;
     dstBuffer.buffer = buf;
     dstBuffer.offset = 0;
     dstBuffer.rowPitch = rowBytes;
     dstBuffer.imageHeight = height;
 
-    dawn::Extent3D copySize = {(uint32_t) width, (uint32_t) height, 1};
+    wgpu::Extent3D copySize = {(uint32_t) width, (uint32_t) height, 1};
     this->getCopyEncoder().CopyTextureToBuffer(&srcTexture, &dstBuffer, &copySize);
     flush();
 
@@ -597,23 +546,24 @@
     SkASSERT(!"unimplemented");
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrDawnGpu::makeSemaphore(bool isOwned) {
+std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrDawnGpu::makeSemaphore(bool isOwned) {
     SkASSERT(!"unimplemented");
     return nullptr;
 }
 
-sk_sp<GrSemaphore> GrDawnGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                   GrResourceProvider::SemaphoreWrapType wrapType,
-                                                   GrWrapOwnership ownership) {
+std::unique_ptr<GrSemaphore> GrDawnGpu::wrapBackendSemaphore(
+        const GrBackendSemaphore& semaphore,
+        GrResourceProvider::SemaphoreWrapType wrapType,
+        GrWrapOwnership ownership) {
     SkASSERT(!"unimplemented");
     return nullptr;
 }
 
-void GrDawnGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrDawnGpu::insertSemaphore(GrSemaphore* semaphore) {
     SkASSERT(!"unimplemented");
 }
 
-void GrDawnGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrDawnGpu::waitSemaphore(GrSemaphore* semaphore) {
     SkASSERT(!"unimplemented");
 }
 
@@ -621,18 +571,17 @@
     SkASSERT(!"unimplemented");
 }
 
-sk_sp<GrSemaphore> GrDawnGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
+std::unique_ptr<GrSemaphore> GrDawnGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
     SkASSERT(!"unimplemented");
     return nullptr;
 }
 
 sk_sp<GrDawnProgram> GrDawnGpu::getOrCreateRenderPipeline(
         GrRenderTarget* rt,
-        const GrProgramInfo& programInfo,
-        GrPrimitiveType primitiveType) {
-    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
-    Desc desc;
-    if (!Desc::Build(&desc, rt, programInfo, primitiveType, hasDepthStencil, this)) {
+        const GrProgramInfo& programInfo) {
+
+    GrProgramDesc desc = this->caps()->makeDesc(rt, programInfo);
+    if (!desc.isValid()) {
         return nullptr;
     }
 
@@ -640,33 +589,35 @@
         return *program;
     }
 
-    dawn::TextureFormat colorFormat;
-    SkAssertResult(GrPixelConfigToDawnFormat(rt->config(), &colorFormat));
-    dawn::TextureFormat stencilFormat = dawn::TextureFormat::Depth24PlusStencil8;
+    wgpu::TextureFormat colorFormat;
+    SkAssertResult(programInfo.backendFormat().asDawnFormat(&colorFormat));
+
+    wgpu::TextureFormat stencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
+    bool hasDepthStencil = rt->renderTargetPriv().getStencilAttachment() != nullptr;
 
     sk_sp<GrDawnProgram> program = GrDawnProgramBuilder::Build(
-        this, rt, programInfo, primitiveType, colorFormat,
+        this, rt, programInfo, colorFormat,
         hasDepthStencil, stencilFormat, &desc);
     fRenderPipelineCache.insert(desc, program);
     return program;
 }
 
-dawn::Sampler GrDawnGpu::getOrCreateSampler(const GrSamplerState& samplerState) {
+wgpu::Sampler GrDawnGpu::getOrCreateSampler(const GrSamplerState& samplerState) {
     auto i = fSamplers.find(samplerState);
     if (i != fSamplers.end()) {
         return i->second;
     }
-    dawn::SamplerDescriptor desc;
+    wgpu::SamplerDescriptor desc;
     desc.addressModeU = to_dawn_address_mode(samplerState.wrapModeX());
     desc.addressModeV = to_dawn_address_mode(samplerState.wrapModeY());
-    desc.addressModeW = dawn::AddressMode::ClampToEdge;
+    desc.addressModeW = wgpu::AddressMode::ClampToEdge;
     desc.magFilter = desc.minFilter = to_dawn_filter_mode(samplerState.filter());
-    desc.mipmapFilter = dawn::FilterMode::Linear;
+    desc.mipmapFilter = wgpu::FilterMode::Linear;
     desc.lodMinClamp = 0.0f;
     desc.lodMaxClamp = 1000.0f;
-    desc.compare = dawn::CompareFunction::Never;
-    dawn::Sampler sampler = device().CreateSampler(&desc);
-    fSamplers.insert(std::pair<GrSamplerState, dawn::Sampler>(samplerState, sampler));
+    desc.compare = wgpu::CompareFunction::Never;
+    wgpu::Sampler sampler = device().CreateSampler(&desc);
+    fSamplers.insert(std::pair<GrSamplerState, wgpu::Sampler>(samplerState, sampler));
     return sampler;
 }
 
@@ -678,13 +629,13 @@
     return fStagingManager.findOrCreateStagingBuffer(size);
 }
 
-void GrDawnGpu::appendCommandBuffer(dawn::CommandBuffer commandBuffer) {
+void GrDawnGpu::appendCommandBuffer(wgpu::CommandBuffer commandBuffer) {
     if (commandBuffer) {
         fCommandBuffers.push_back(commandBuffer);
     }
 }
 
-dawn::CommandEncoder GrDawnGpu::getCopyEncoder() {
+wgpu::CommandEncoder GrDawnGpu::getCopyEncoder() {
     if (!fCopyEncoder) {
         fCopyEncoder = fDevice.CreateCommandEncoder();
     }
diff --git a/src/gpu/dawn/GrDawnGpu.h b/src/gpu/dawn/GrDawnGpu.h
index 96cb54b..c909630 100644
--- a/src/gpu/dawn/GrDawnGpu.h
+++ b/src/gpu/dawn/GrDawnGpu.h
@@ -9,6 +9,7 @@
 #define GrDawnGpu_DEFINED
 
 #include "src/gpu/GrGpu.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "dawn/dawncpp.h"
 #include "src/core/SkLRUCache.h"
 #include "src/gpu/dawn/GrDawnRingBuffer.h"
@@ -26,25 +27,23 @@
 
 class GrDawnGpu : public GrGpu {
 public:
-    static sk_sp<GrGpu> Make(const dawn::Device& device, const GrContextOptions&, GrContext*);
-    GrDawnGpu(GrContext* context, const GrContextOptions& options, const dawn::Device& device);
+    static sk_sp<GrGpu> Make(const wgpu::Device& device, const GrContextOptions&, GrContext*);
+    GrDawnGpu(GrContext* context, const GrContextOptions& options, const wgpu::Device& device);
 
     ~GrDawnGpu() override;
 
     void disconnect(DisconnectType) override;
 
-    const dawn::Device& device() const { return fDevice; }
-    const dawn::Queue&  queue() const { return fQueue; }
+    const wgpu::Device& device() const { return fDevice; }
+    const wgpu::Queue&  queue() const { return fQueue; }
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
-    GrBackendTexture onCreateBackendTexture(int w, int h,
+    GrBackendTexture onCreateBackendTexture(SkISize,
                                             const GrBackendFormat&,
-                                            GrMipMapped,
                                             GrRenderable,
-                                            const SkPixmap srcData[],
+                                            const BackendTextureData* data,
                                             int numMipLevels,
-                                            const SkColor4f* color,
                                             GrProtected isProtected) override;
     void deleteBackendTexture(const GrBackendTexture&) override;
 #if GR_TEST_UTILS
@@ -66,7 +65,7 @@
             GrRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) override;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) override;
 
     SkSL::Compiler* shaderCompiler() const {
         return fCompiler.get();
@@ -78,28 +77,27 @@
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true) override;
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                            GrResourceProvider::SemaphoreWrapType wrapType,
-                                            GrWrapOwnership ownership) override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
-    void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true) override;
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(
+            const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType,
+            GrWrapOwnership ownership) override;
+    void insertSemaphore(GrSemaphore* semaphore) override;
+    void waitSemaphore(GrSemaphore* semaphore) override;
     void checkFinishProcs() override;
 
-    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
+    std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
-    sk_sp<GrDawnProgram> getOrCreateRenderPipeline(GrRenderTarget*,
-                                                   const GrProgramInfo& programInfo,
-                                                   GrPrimitiveType primitiveType);
+    sk_sp<GrDawnProgram> getOrCreateRenderPipeline(GrRenderTarget*, const GrProgramInfo&);
 
-    dawn::Sampler getOrCreateSampler(const GrSamplerState& samplerState);
+    wgpu::Sampler getOrCreateSampler(const GrSamplerState& samplerState);
 
     GrDawnRingBuffer::Slice allocateUniformRingBufferSlice(int size);
     GrDawnStagingBuffer* getStagingBuffer(size_t size);
     GrDawnStagingManager* getStagingManager() { return &fStagingManager; }
-    dawn::CommandEncoder getCopyEncoder();
+    wgpu::CommandEncoder getCopyEncoder();
     void flushCopyEncoder();
-    void appendCommandBuffer(dawn::CommandBuffer commandBuffer);
+    void appendCommandBuffer(wgpu::CommandBuffer commandBuffer);
 
 private:
     void onResetContext(uint32_t resetBits) override {}
@@ -158,16 +156,16 @@
     bool onCopySurface(GrSurface* dst, GrSurface* src,
                        const SkIRect& srcRect, const SkIPoint& dstPoint) override;
 
-    void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+    bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                        const GrFlushInfo& info, const GrPrepareForExternalIORequests&) override;
 
-    dawn::Device                                    fDevice;
-    dawn::Queue                                     fQueue;
+    wgpu::Device                                    fDevice;
+    wgpu::Queue                                     fQueue;
     std::unique_ptr<SkSL::Compiler>                 fCompiler;
     std::unique_ptr<GrDawnOpsRenderPass>            fOpsRenderPass;
     GrDawnRingBuffer                                fUniformRingBuffer;
-    dawn::CommandEncoder                            fCopyEncoder;
-    std::vector<dawn::CommandBuffer>                fCommandBuffers;
+    wgpu::CommandEncoder                            fCopyEncoder;
+    std::vector<wgpu::CommandBuffer>                fCommandBuffers;
 
     struct ProgramDescHash {
         uint32_t operator()(const GrProgramDesc& desc) const {
@@ -182,7 +180,7 @@
     };
 
     SkLRUCache<GrProgramDesc, sk_sp<GrDawnProgram>, ProgramDescHash>    fRenderPipelineCache;
-    std::unordered_map<GrSamplerState, dawn::Sampler, SamplerHash> fSamplers;
+    std::unordered_map<GrSamplerState, wgpu::Sampler, SamplerHash> fSamplers;
     GrDawnStagingManager fStagingManager;
 
     typedef GrGpu INHERITED;
diff --git a/src/gpu/dawn/GrDawnOpsRenderPass.cpp b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
index 144f16b..10a58c2 100644
--- a/src/gpu/dawn/GrDawnOpsRenderPass.cpp
+++ b/src/gpu/dawn/GrDawnOpsRenderPass.cpp
@@ -24,19 +24,19 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static dawn::LoadOp to_dawn_load_op(GrLoadOp loadOp) {
+static wgpu::LoadOp to_dawn_load_op(GrLoadOp loadOp) {
     switch (loadOp) {
         case GrLoadOp::kLoad:
-            return dawn::LoadOp::Load;
+            return wgpu::LoadOp::Load;
         case GrLoadOp::kDiscard:
             // Use LoadOp::Load to emulate DontCare.
             // Dawn doesn't have DontCare, for security reasons.
             // Load should be equivalent to DontCare for desktop; Clear would
             // probably be better for tilers. If Dawn does add DontCare
             // as an extension, use it here.
-            return dawn::LoadOp::Load;
+            return wgpu::LoadOp::Load;
         case GrLoadOp::kClear:
-            return dawn::LoadOp::Clear;
+            return wgpu::LoadOp::Clear;
         default:
             SK_ABORT("Invalid LoadOp");
     }
@@ -49,40 +49,40 @@
         , fGpu(gpu)
         , fColorInfo(colorInfo) {
     fEncoder = fGpu->device().CreateCommandEncoder();
-    dawn::LoadOp colorOp = to_dawn_load_op(colorInfo.fLoadOp);
-    dawn::LoadOp stencilOp = to_dawn_load_op(stencilInfo.fLoadOp);
+    wgpu::LoadOp colorOp = to_dawn_load_op(colorInfo.fLoadOp);
+    wgpu::LoadOp stencilOp = to_dawn_load_op(stencilInfo.fLoadOp);
     fPassEncoder = beginRenderPass(colorOp, stencilOp);
 }
 
-dawn::RenderPassEncoder GrDawnOpsRenderPass::beginRenderPass(dawn::LoadOp colorOp,
-                                                             dawn::LoadOp stencilOp) {
-    dawn::Texture texture = static_cast<GrDawnRenderTarget*>(fRenderTarget)->texture();
+wgpu::RenderPassEncoder GrDawnOpsRenderPass::beginRenderPass(wgpu::LoadOp colorOp,
+                                                             wgpu::LoadOp stencilOp) {
+    wgpu::Texture texture = static_cast<GrDawnRenderTarget*>(fRenderTarget)->texture();
     auto stencilAttachment = static_cast<GrDawnStencilAttachment*>(
         fRenderTarget->renderTargetPriv().getStencilAttachment());
-    dawn::TextureViewDescriptor desc;
+    wgpu::TextureViewDescriptor desc;
     desc.mipLevelCount = 1;
-    dawn::TextureView colorView = texture.CreateView(&desc);
+    wgpu::TextureView colorView = texture.CreateView(&desc);
     const float *c = fColorInfo.fClearColor.vec();
 
-    dawn::RenderPassColorAttachmentDescriptor colorAttachment;
+    wgpu::RenderPassColorAttachmentDescriptor colorAttachment;
     colorAttachment.attachment = colorView;
     colorAttachment.resolveTarget = nullptr;
     colorAttachment.clearColor = { c[0], c[1], c[2], c[3] };
     colorAttachment.loadOp = colorOp;
-    colorAttachment.storeOp = dawn::StoreOp::Store;
-    dawn::RenderPassColorAttachmentDescriptor* colorAttachments = { &colorAttachment };
-    dawn::RenderPassDescriptor renderPassDescriptor;
+    colorAttachment.storeOp = wgpu::StoreOp::Store;
+    wgpu::RenderPassColorAttachmentDescriptor* colorAttachments = { &colorAttachment };
+    wgpu::RenderPassDescriptor renderPassDescriptor;
     renderPassDescriptor.colorAttachmentCount = 1;
     renderPassDescriptor.colorAttachments = colorAttachments;
     if (stencilAttachment) {
-        dawn::RenderPassDepthStencilAttachmentDescriptor depthStencilAttachment;
+        wgpu::RenderPassDepthStencilAttachmentDescriptor depthStencilAttachment;
         depthStencilAttachment.attachment = stencilAttachment->view();
         depthStencilAttachment.depthLoadOp = stencilOp;
         depthStencilAttachment.stencilLoadOp = stencilOp;
         depthStencilAttachment.clearDepth = 1.0f;
         depthStencilAttachment.clearStencil = 0;
-        depthStencilAttachment.depthStoreOp = dawn::StoreOp::Store;
-        depthStencilAttachment.stencilStoreOp = dawn::StoreOp::Store;
+        depthStencilAttachment.depthStoreOp = wgpu::StoreOp::Store;
+        depthStencilAttachment.stencilStoreOp = wgpu::StoreOp::Store;
         renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment;
     } else {
         renderPassDescriptor.depthStencilAttachment = nullptr;
@@ -109,12 +109,12 @@
 
 void GrDawnOpsRenderPass::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
     fPassEncoder.EndPass();
-    fPassEncoder = beginRenderPass(dawn::LoadOp::Load, dawn::LoadOp::Clear);
+    fPassEncoder = beginRenderPass(wgpu::LoadOp::Load, wgpu::LoadOp::Clear);
 }
 
 void GrDawnOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
     fPassEncoder.EndPass();
-    fPassEncoder = beginRenderPass(dawn::LoadOp::Clear, dawn::LoadOp::Load);
+    fPassEncoder = beginRenderPass(wgpu::LoadOp::Clear, wgpu::LoadOp::Load);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -141,21 +141,17 @@
     fPassEncoder.SetScissorRect(rect.x(), rect.y(), rect.width(), rect.height());
 }
 
-void GrDawnOpsRenderPass::applyState(const GrProgramInfo& programInfo,
-                                     const GrPrimitiveType primitiveType) {
-    sk_sp<GrDawnProgram> program = fGpu->getOrCreateRenderPipeline(fRenderTarget,
-                                                                   programInfo,
-                                                                   primitiveType);
-    auto bindGroup = program->setData(fGpu, fRenderTarget, programInfo);
+void GrDawnOpsRenderPass::applyState(GrDawnProgram* program, const GrProgramInfo& programInfo) {
+    auto bindGroup = program->setUniformData(fGpu, fRenderTarget, programInfo);
     fPassEncoder.SetPipeline(program->fRenderPipeline);
     fPassEncoder.SetBindGroup(0, bindGroup, 0, nullptr);
     const GrPipeline& pipeline = programInfo.pipeline();
     if (pipeline.isStencilEnabled()) {
-        fPassEncoder.SetStencilReference(pipeline.getUserStencil()->fFront.fRef);
+        fPassEncoder.SetStencilReference(pipeline.getUserStencil()->fCCWFace.fRef);
     }
     GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
     const float* c = blendInfo.fBlendConstant.vec();
-    dawn::Color color{c[0], c[1], c[2], c[3]};
+    wgpu::Color color{c[0], c[1], c[2], c[3]};
     fPassEncoder.SetBlendColor(&color);
     this->setScissorState(programInfo);
 }
@@ -167,8 +163,21 @@
     if (!meshCount) {
         return;
     }
+    sk_sp<GrDawnProgram> program = fGpu->getOrCreateRenderPipeline(fRenderTarget, programInfo);
+    if (!programInfo.hasDynamicPrimProcTextures()) {
+        auto textures = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
+                                                               : nullptr;
+        auto bindGroup = program->setTextures(fGpu, programInfo, textures);
+        fPassEncoder.SetBindGroup(1, bindGroup, 0, nullptr);
+    }
     for (int i = 0; i < meshCount; ++i) {
-        applyState(programInfo, meshes[0].primitiveType());
+        SkASSERT(meshes[i].primitiveType() == programInfo.primitiveType());
+        if (programInfo.hasDynamicPrimProcTextures()) {
+            auto textures = programInfo.dynamicPrimProcTextures(i);
+            auto bindGroup = program->setTextures(fGpu, programInfo, textures);
+            fPassEncoder.SetBindGroup(1, bindGroup, 0, nullptr);
+        }
+        this->applyState(program.get(), programInfo);
         meshes[i].sendToGpu(this);
     }
 }
@@ -180,7 +189,7 @@
                                                  const GrBuffer* instanceBuffer,
                                                  int instanceCount,
                                                  int baseInstance) {
-    dawn::Buffer vb = static_cast<const GrDawnBuffer*>(vertexBuffer)->get();
+    wgpu::Buffer vb = static_cast<const GrDawnBuffer*>(vertexBuffer)->get();
     fPassEncoder.SetVertexBuffer(0, vb);
     fPassEncoder.Draw(vertexCount, 1, baseVertex, baseInstance);
     fGpu->stats()->incNumDraws();
@@ -196,8 +205,8 @@
                                                         int instanceCount,
                                                         int baseInstance,
                                                         GrPrimitiveRestart restart) {
-    dawn::Buffer vb = static_cast<const GrDawnBuffer*>(vertexBuffer)->get();
-    dawn::Buffer ib = static_cast<const GrDawnBuffer*>(indexBuffer)->get();
+    wgpu::Buffer vb = static_cast<const GrDawnBuffer*>(vertexBuffer)->get();
+    wgpu::Buffer ib = static_cast<const GrDawnBuffer*>(indexBuffer)->get();
     fPassEncoder.SetIndexBuffer(ib);
     fPassEncoder.SetVertexBuffer(0, vb);
     fPassEncoder.DrawIndexed(indexCount, 1, baseIndex, baseVertex, baseInstance);
diff --git a/src/gpu/dawn/GrDawnOpsRenderPass.h b/src/gpu/dawn/GrDawnOpsRenderPass.h
index a1a90362..0e612a4 100644
--- a/src/gpu/dawn/GrDawnOpsRenderPass.h
+++ b/src/gpu/dawn/GrDawnOpsRenderPass.h
@@ -17,6 +17,7 @@
 
 class GrDawnGpu;
 class GrDawnRenderTarget;
+struct GrDawnProgram;
 
 class GrDawnOpsRenderPass : public GrOpsRenderPass, private GrMesh::SendToGpuImpl {
 public:
@@ -28,7 +29,7 @@
     void begin() override { }
     void end() override;
 
-    dawn::RenderPassEncoder beginRenderPass(dawn::LoadOp colorOp, dawn::LoadOp stencilOp);
+    wgpu::RenderPassEncoder beginRenderPass(wgpu::LoadOp colorOp, wgpu::LoadOp stencilOp);
     void insertEventMarker(const char*) override;
 
     void inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) override;
@@ -39,8 +40,7 @@
     GrGpu* gpu() override;
 
     void setScissorState(const GrProgramInfo&);
-    void applyState(const GrProgramInfo& programInfo,
-                    const GrPrimitiveType primitiveType);
+    void applyState(GrDawnProgram*, const GrProgramInfo& programInfo);
 
     void onDraw(const GrProgramInfo& programInfo,
                 const GrMesh mesh[],
@@ -86,8 +86,8 @@
     };
 
     GrDawnGpu*                  fGpu;
-    dawn::CommandEncoder        fEncoder;
-    dawn::RenderPassEncoder     fPassEncoder;
+    wgpu::CommandEncoder        fEncoder;
+    wgpu::RenderPassEncoder     fPassEncoder;
     LoadAndStoreInfo            fColorInfo;
 
     typedef GrOpsRenderPass     INHERITED;
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.cpp b/src/gpu/dawn/GrDawnProgramBuilder.cpp
index d8c0e0b..d7bf830 100644
--- a/src/gpu/dawn/GrDawnProgramBuilder.cpp
+++ b/src/gpu/dawn/GrDawnProgramBuilder.cpp
@@ -15,11 +15,12 @@
 #include "src/sksl/SkSLCompiler.h"
 
 static SkSL::String sksl_to_spirv(const GrDawnGpu* gpu, const char* shaderString,
-                                  SkSL::Program::Kind kind, bool flipY,
+                                  SkSL::Program::Kind kind, bool flipY, uint32_t rtHeightOffset,
                                   SkSL::Program::Inputs* inputs) {
     SkSL::Program::Settings settings;
     settings.fCaps = gpu->caps()->shaderCaps();
     settings.fFlipY = flipY;
+    settings.fRTHeightOffset = rtHeightOffset;
     std::unique_ptr<SkSL::Program> program = gpu->shaderCompiler()->convertProgram(
         kind,
         shaderString,
@@ -37,32 +38,32 @@
     return code;
 }
 
-static dawn::BlendFactor to_dawn_blend_factor(GrBlendCoeff coeff) {
+static wgpu::BlendFactor to_dawn_blend_factor(GrBlendCoeff coeff) {
     switch (coeff) {
     case kZero_GrBlendCoeff:
-        return dawn::BlendFactor::Zero;
+        return wgpu::BlendFactor::Zero;
     case kOne_GrBlendCoeff:
-        return dawn::BlendFactor::One;
+        return wgpu::BlendFactor::One;
     case kSC_GrBlendCoeff:
-        return dawn::BlendFactor::SrcColor;
+        return wgpu::BlendFactor::SrcColor;
     case kISC_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusSrcColor;
+        return wgpu::BlendFactor::OneMinusSrcColor;
     case kDC_GrBlendCoeff:
-        return dawn::BlendFactor::DstColor;
+        return wgpu::BlendFactor::DstColor;
     case kIDC_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusDstColor;
+        return wgpu::BlendFactor::OneMinusDstColor;
     case kSA_GrBlendCoeff:
-        return dawn::BlendFactor::SrcAlpha;
+        return wgpu::BlendFactor::SrcAlpha;
     case kISA_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusSrcAlpha;
+        return wgpu::BlendFactor::OneMinusSrcAlpha;
     case kDA_GrBlendCoeff:
-        return dawn::BlendFactor::DstAlpha;
+        return wgpu::BlendFactor::DstAlpha;
     case kIDA_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusDstAlpha;
+        return wgpu::BlendFactor::OneMinusDstAlpha;
     case kConstC_GrBlendCoeff:
-        return dawn::BlendFactor::BlendColor;
+        return wgpu::BlendFactor::BlendColor;
     case kIConstC_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusBlendColor;
+        return wgpu::BlendFactor::OneMinusBlendColor;
     case kConstA_GrBlendCoeff:
     case kIConstA_GrBlendCoeff:
     case kS2C_GrBlendCoeff:
@@ -71,152 +72,152 @@
     case kIS2A_GrBlendCoeff:
     default:
         SkASSERT(!"unsupported blend coefficient");
-        return dawn::BlendFactor::One;
+        return wgpu::BlendFactor::One;
     }
 }
 
-static dawn::BlendFactor to_dawn_blend_factor_for_alpha(GrBlendCoeff coeff) {
+static wgpu::BlendFactor to_dawn_blend_factor_for_alpha(GrBlendCoeff coeff) {
     switch (coeff) {
     // Force all srcColor used in alpha slot to alpha version.
     case kSC_GrBlendCoeff:
-        return dawn::BlendFactor::SrcAlpha;
+        return wgpu::BlendFactor::SrcAlpha;
     case kISC_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusSrcAlpha;
+        return wgpu::BlendFactor::OneMinusSrcAlpha;
     case kDC_GrBlendCoeff:
-        return dawn::BlendFactor::DstAlpha;
+        return wgpu::BlendFactor::DstAlpha;
     case kIDC_GrBlendCoeff:
-        return dawn::BlendFactor::OneMinusDstAlpha;
+        return wgpu::BlendFactor::OneMinusDstAlpha;
     default:
         return to_dawn_blend_factor(coeff);
     }
 }
 
-static dawn::BlendOperation to_dawn_blend_operation(GrBlendEquation equation) {
+static wgpu::BlendOperation to_dawn_blend_operation(GrBlendEquation equation) {
     switch (equation) {
     case kAdd_GrBlendEquation:
-        return dawn::BlendOperation::Add;
+        return wgpu::BlendOperation::Add;
     case kSubtract_GrBlendEquation:
-        return dawn::BlendOperation::Subtract;
+        return wgpu::BlendOperation::Subtract;
     case kReverseSubtract_GrBlendEquation:
-        return dawn::BlendOperation::ReverseSubtract;
+        return wgpu::BlendOperation::ReverseSubtract;
     default:
         SkASSERT(!"unsupported blend equation");
-        return dawn::BlendOperation::Add;
+        return wgpu::BlendOperation::Add;
     }
 }
 
-static dawn::CompareFunction to_dawn_compare_function(GrStencilTest test) {
+static wgpu::CompareFunction to_dawn_compare_function(GrStencilTest test) {
     switch (test) {
         case GrStencilTest::kAlways:
-            return dawn::CompareFunction::Always;
+            return wgpu::CompareFunction::Always;
         case GrStencilTest::kNever:
-            return dawn::CompareFunction::Never;
+            return wgpu::CompareFunction::Never;
         case GrStencilTest::kGreater:
-            return dawn::CompareFunction::Greater;
+            return wgpu::CompareFunction::Greater;
         case GrStencilTest::kGEqual:
-            return dawn::CompareFunction::GreaterEqual;
+            return wgpu::CompareFunction::GreaterEqual;
         case GrStencilTest::kLess:
-            return dawn::CompareFunction::Less;
+            return wgpu::CompareFunction::Less;
         case GrStencilTest::kLEqual:
-            return dawn::CompareFunction::LessEqual;
+            return wgpu::CompareFunction::LessEqual;
         case GrStencilTest::kEqual:
-            return dawn::CompareFunction::Equal;
+            return wgpu::CompareFunction::Equal;
         case GrStencilTest::kNotEqual:
-            return dawn::CompareFunction::NotEqual;
+            return wgpu::CompareFunction::NotEqual;
         default:
             SkASSERT(!"unsupported stencil test");
-            return dawn::CompareFunction::Always;
+            return wgpu::CompareFunction::Always;
     }
 }
 
-static dawn::StencilOperation to_dawn_stencil_operation(GrStencilOp op) {
+static wgpu::StencilOperation to_dawn_stencil_operation(GrStencilOp op) {
     switch (op) {
         case GrStencilOp::kKeep:
-            return dawn::StencilOperation::Keep;
+            return wgpu::StencilOperation::Keep;
         case GrStencilOp::kZero:
-            return dawn::StencilOperation::Zero;
+            return wgpu::StencilOperation::Zero;
         case GrStencilOp::kReplace:
-            return dawn::StencilOperation::Replace;
+            return wgpu::StencilOperation::Replace;
         case GrStencilOp::kInvert:
-            return dawn::StencilOperation::Invert;
+            return wgpu::StencilOperation::Invert;
         case GrStencilOp::kIncClamp:
-            return dawn::StencilOperation::IncrementClamp;
+            return wgpu::StencilOperation::IncrementClamp;
         case GrStencilOp::kDecClamp:
-            return dawn::StencilOperation::DecrementClamp;
+            return wgpu::StencilOperation::DecrementClamp;
         case GrStencilOp::kIncWrap:
-            return dawn::StencilOperation::IncrementWrap;
+            return wgpu::StencilOperation::IncrementWrap;
         case GrStencilOp::kDecWrap:
-            return dawn::StencilOperation::DecrementWrap;
+            return wgpu::StencilOperation::DecrementWrap;
         default:
             SkASSERT(!"unsupported stencil function");
-            return dawn::StencilOperation::Keep;
+            return wgpu::StencilOperation::Keep;
     }
 }
 
-static dawn::PrimitiveTopology to_dawn_primitive_topology(GrPrimitiveType primitiveType) {
+static wgpu::PrimitiveTopology to_dawn_primitive_topology(GrPrimitiveType primitiveType) {
     switch (primitiveType) {
         case GrPrimitiveType::kTriangles:
-            return dawn::PrimitiveTopology::TriangleList;
+            return wgpu::PrimitiveTopology::TriangleList;
         case GrPrimitiveType::kTriangleStrip:
-            return dawn::PrimitiveTopology::TriangleStrip;
+            return wgpu::PrimitiveTopology::TriangleStrip;
         case GrPrimitiveType::kPoints:
-            return dawn::PrimitiveTopology::PointList;
+            return wgpu::PrimitiveTopology::PointList;
         case GrPrimitiveType::kLines:
-            return dawn::PrimitiveTopology::LineList;
+            return wgpu::PrimitiveTopology::LineList;
         case GrPrimitiveType::kLineStrip:
-            return dawn::PrimitiveTopology::LineStrip;
+            return wgpu::PrimitiveTopology::LineStrip;
         case GrPrimitiveType::kPath:
         default:
             SkASSERT(!"unsupported primitive topology");
-            return dawn::PrimitiveTopology::TriangleList;
+            return wgpu::PrimitiveTopology::TriangleList;
     }
 }
 
-static dawn::VertexFormat to_dawn_vertex_format(GrVertexAttribType type) {
+static wgpu::VertexFormat to_dawn_vertex_format(GrVertexAttribType type) {
     switch (type) {
     case kFloat_GrVertexAttribType:
     case kHalf_GrVertexAttribType:
-        return dawn::VertexFormat::Float;
+        return wgpu::VertexFormat::Float;
     case kFloat2_GrVertexAttribType:
     case kHalf2_GrVertexAttribType:
-        return dawn::VertexFormat::Float2;
+        return wgpu::VertexFormat::Float2;
     case kFloat3_GrVertexAttribType:
     case kHalf3_GrVertexAttribType:
-        return dawn::VertexFormat::Float3;
+        return wgpu::VertexFormat::Float3;
     case kFloat4_GrVertexAttribType:
     case kHalf4_GrVertexAttribType:
-        return dawn::VertexFormat::Float4;
+        return wgpu::VertexFormat::Float4;
     case kUShort2_GrVertexAttribType:
-        return dawn::VertexFormat::UShort2;
+        return wgpu::VertexFormat::UShort2;
     case kInt_GrVertexAttribType:
-        return dawn::VertexFormat::Int;
+        return wgpu::VertexFormat::Int;
     case kUByte4_norm_GrVertexAttribType:
-        return dawn::VertexFormat::UChar4Norm;
+        return wgpu::VertexFormat::UChar4Norm;
     default:
         SkASSERT(!"unsupported vertex format");
-        return dawn::VertexFormat::Float4;
+        return wgpu::VertexFormat::Float4;
     }
 }
 
-static dawn::ColorStateDescriptor create_color_state(const GrDawnGpu* gpu,
+static wgpu::ColorStateDescriptor create_color_state(const GrDawnGpu* gpu,
                                                      const GrPipeline& pipeline,
-                                                     dawn::TextureFormat colorFormat) {
+                                                     wgpu::TextureFormat colorFormat) {
     GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
     GrBlendEquation equation = blendInfo.fEquation;
     GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
 
-    dawn::BlendFactor srcFactor = to_dawn_blend_factor(srcCoeff);
-    dawn::BlendFactor dstFactor = to_dawn_blend_factor(dstCoeff);
-    dawn::BlendFactor srcFactorAlpha = to_dawn_blend_factor_for_alpha(srcCoeff);
-    dawn::BlendFactor dstFactorAlpha = to_dawn_blend_factor_for_alpha(dstCoeff);
-    dawn::BlendOperation operation = to_dawn_blend_operation(equation);
-    auto mask = blendInfo.fWriteColor ? dawn::ColorWriteMask::All : dawn::ColorWriteMask::None;
+    wgpu::BlendFactor srcFactor = to_dawn_blend_factor(srcCoeff);
+    wgpu::BlendFactor dstFactor = to_dawn_blend_factor(dstCoeff);
+    wgpu::BlendFactor srcFactorAlpha = to_dawn_blend_factor_for_alpha(srcCoeff);
+    wgpu::BlendFactor dstFactorAlpha = to_dawn_blend_factor_for_alpha(dstCoeff);
+    wgpu::BlendOperation operation = to_dawn_blend_operation(equation);
+    auto mask = blendInfo.fWriteColor ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
 
-    dawn::BlendDescriptor colorDesc = {operation, srcFactor, dstFactor};
-    dawn::BlendDescriptor alphaDesc = {operation, srcFactorAlpha, dstFactorAlpha};
+    wgpu::BlendDescriptor colorDesc = {operation, srcFactor, dstFactor};
+    wgpu::BlendDescriptor alphaDesc = {operation, srcFactorAlpha, dstFactorAlpha};
 
-    dawn::ColorStateDescriptor descriptor;
+    wgpu::ColorStateDescriptor descriptor;
     descriptor.format = colorFormat;
     descriptor.alphaBlend = alphaDesc;
     descriptor.colorBlend = colorDesc;
@@ -226,30 +227,32 @@
     return descriptor;
 }
 
-static dawn::StencilStateFaceDescriptor to_stencil_state_face(const GrStencilSettings::Face& face) {
-     dawn::StencilStateFaceDescriptor desc;
+static wgpu::StencilStateFaceDescriptor to_stencil_state_face(const GrStencilSettings::Face& face) {
+     wgpu::StencilStateFaceDescriptor desc;
      desc.compare = to_dawn_compare_function(face.fTest);
      desc.failOp = desc.depthFailOp = to_dawn_stencil_operation(face.fFailOp);
      desc.passOp = to_dawn_stencil_operation(face.fPassOp);
      return desc;
 }
 
-static dawn::DepthStencilStateDescriptor create_depth_stencil_state(
-        const GrStencilSettings& stencilSettings,
-        dawn::TextureFormat depthStencilFormat,
-        GrSurfaceOrigin origin) {
-    dawn::DepthStencilStateDescriptor state;
+static wgpu::DepthStencilStateDescriptor create_depth_stencil_state(
+        const GrProgramInfo& programInfo,
+        wgpu::TextureFormat depthStencilFormat) {
+    GrStencilSettings stencilSettings = programInfo.nonGLStencilSettings();
+    GrSurfaceOrigin origin = programInfo.origin();
+
+    wgpu::DepthStencilStateDescriptor state;
     state.format = depthStencilFormat;
     if (!stencilSettings.isDisabled()) {
         if (stencilSettings.isTwoSided()) {
-            auto front = stencilSettings.front(origin);
-            auto back = stencilSettings.front(origin);
+            auto front = stencilSettings.postOriginCCWFace(origin);
+            auto back = stencilSettings.postOriginCWFace(origin);
             state.stencilFront = to_stencil_state_face(front);
             state.stencilBack = to_stencil_state_face(back);
             state.stencilReadMask = front.fTestMask;
             state.stencilWriteMask = front.fWriteMask;
         } else {
-            auto frontAndBack = stencilSettings.frontAndBack();
+            auto frontAndBack = stencilSettings.singleSidedFace();
             state.stencilBack = state.stencilFront = to_stencil_state_face(frontAndBack);
             state.stencilReadMask = frontAndBack.fTestMask;
             state.stencilWriteMask = frontAndBack.fWriteMask;
@@ -258,11 +261,11 @@
     return state;
 }
 
-static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding, const dawn::Buffer& buffer,
+static wgpu::BindGroupBinding make_bind_group_binding(uint32_t binding, const wgpu::Buffer& buffer,
                                                       uint32_t offset, uint32_t size, const
-                                                      dawn::Sampler& sampler,
-                                                      const dawn::TextureView& textureView) {
-    dawn::BindGroupBinding result;
+                                                      wgpu::Sampler& sampler,
+                                                      const wgpu::TextureView& textureView) {
+    wgpu::BindGroupBinding result;
     result.binding = binding;
     result.buffer = buffer;
     result.offset = offset;
@@ -272,28 +275,27 @@
     return result;
 }
 
-static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding, const dawn::Buffer& buffer,
+static wgpu::BindGroupBinding make_bind_group_binding(uint32_t binding, const wgpu::Buffer& buffer,
                                                       uint32_t offset, uint32_t size) {
     return make_bind_group_binding(binding, buffer, offset, size, nullptr, nullptr);
 }
 
-static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding,
-                                                      const dawn::Sampler& sampler) {
+static wgpu::BindGroupBinding make_bind_group_binding(uint32_t binding,
+                                                      const wgpu::Sampler& sampler) {
     return make_bind_group_binding(binding, nullptr, 0, 0, sampler, nullptr);
 }
 
-static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding,
-                                                      const dawn::TextureView& textureView) {
+static wgpu::BindGroupBinding make_bind_group_binding(uint32_t binding,
+                                                      const wgpu::TextureView& textureView) {
     return make_bind_group_binding(binding, nullptr, 0, 0, nullptr, textureView);
 }
 
 sk_sp<GrDawnProgram> GrDawnProgramBuilder::Build(GrDawnGpu* gpu,
                                                  GrRenderTarget* renderTarget,
                                                  const GrProgramInfo& programInfo,
-                                                 GrPrimitiveType primitiveType,
-                                                 dawn::TextureFormat colorFormat,
+                                                 wgpu::TextureFormat colorFormat,
                                                  bool hasDepthStencil,
-                                                 dawn::TextureFormat depthStencilFormat,
+                                                 wgpu::TextureFormat depthStencilFormat,
                                                  GrProgramDesc* desc) {
     GrDawnProgramBuilder builder(gpu, renderTarget, programInfo, desc);
     if (!builder.emitAndInstallProcs()) {
@@ -308,60 +310,67 @@
     builder.finalizeShaders();
 
     SkSL::Program::Inputs vertInputs, fragInputs;
-    GrDawnUniformHandler::UniformInfoArray& uniforms = builder.fUniformHandler.fUniforms;
-    uint32_t uniformBufferSize = builder.fUniformHandler.fCurrentUBOOffset;
-    sk_sp<GrDawnProgram> result(new GrDawnProgram(uniforms, uniformBufferSize));
     bool flipY = programInfo.origin() != kTopLeft_GrSurfaceOrigin;
     auto vsModule = builder.createShaderModule(builder.fVS, SkSL::Program::kVertex_Kind, flipY,
                                                &vertInputs);
     auto fsModule = builder.createShaderModule(builder.fFS, SkSL::Program::kFragment_Kind, flipY,
                                                &fragInputs);
+    GrDawnUniformHandler::UniformInfoArray& uniforms = builder.fUniformHandler.fUniforms;
+    uint32_t uniformBufferSize = builder.fUniformHandler.fCurrentUBOOffset;
+    sk_sp<GrDawnProgram> result(new GrDawnProgram(uniforms, uniformBufferSize));
     result->fGeometryProcessor = std::move(builder.fGeometryProcessor);
     result->fXferProcessor = std::move(builder.fXferProcessor);
     result->fFragmentProcessors = std::move(builder.fFragmentProcessors);
     result->fFragmentProcessorCnt = builder.fFragmentProcessorCnt;
-    std::vector<dawn::BindGroupLayoutBinding> layoutBindings;
+    std::vector<wgpu::BindGroupLayoutBinding> uniformLayoutBindings;
     if (0 != uniformBufferSize) {
-        layoutBindings.push_back({ GrDawnUniformHandler::kUniformBinding,
-                                   dawn::ShaderStage::Vertex | dawn::ShaderStage::Fragment,
-                                   dawn::BindingType::UniformBuffer});
+        uniformLayoutBindings.push_back({ GrDawnUniformHandler::kUniformBinding,
+                                          wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
+                                          wgpu::BindingType::UniformBuffer});
     }
-    uint32_t binding = GrDawnUniformHandler::kSamplerBindingBase;
+    wgpu::BindGroupLayoutDescriptor uniformBindGroupLayoutDesc;
+    uniformBindGroupLayoutDesc.bindingCount = uniformLayoutBindings.size();
+    uniformBindGroupLayoutDesc.bindings = uniformLayoutBindings.data();
+    result->fBindGroupLayouts[0] =
+        gpu->device().CreateBindGroupLayout(&uniformBindGroupLayoutDesc);
+    uint32_t binding = 0;
+    std::vector<wgpu::BindGroupLayoutBinding> textureLayoutBindings;
     for (int i = 0; i < builder.fUniformHandler.fSamplers.count(); ++i) {
-        layoutBindings.push_back({ binding++, dawn::ShaderStage::Fragment,
-                                   dawn::BindingType::Sampler});
-        layoutBindings.push_back({ binding++, dawn::ShaderStage::Fragment,
-                                   dawn::BindingType::SampledTexture});
+        textureLayoutBindings.push_back({ binding++, wgpu::ShaderStage::Fragment,
+                                          wgpu::BindingType::Sampler});
+        textureLayoutBindings.push_back({ binding++, wgpu::ShaderStage::Fragment,
+                                          wgpu::BindingType::SampledTexture});
     }
-    dawn::BindGroupLayoutDescriptor bindGroupLayoutDesc;
-    bindGroupLayoutDesc.bindingCount = layoutBindings.size();
-    bindGroupLayoutDesc.bindings = layoutBindings.data();
-    result->fBindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
-    dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
-    pipelineLayoutDesc.bindGroupLayoutCount = 1;
-    pipelineLayoutDesc.bindGroupLayouts = &result->fBindGroupLayout;
+    wgpu::BindGroupLayoutDescriptor textureBindGroupLayoutDesc;
+    textureBindGroupLayoutDesc.bindingCount = textureLayoutBindings.size();
+    textureBindGroupLayoutDesc.bindings = textureLayoutBindings.data();
+    result->fBindGroupLayouts[1] =
+        gpu->device().CreateBindGroupLayout(&textureBindGroupLayoutDesc);
+    wgpu::PipelineLayoutDescriptor pipelineLayoutDesc;
+    pipelineLayoutDesc.bindGroupLayoutCount = 2;
+    pipelineLayoutDesc.bindGroupLayouts = &result->fBindGroupLayouts[0];
     auto pipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc);
     result->fBuiltinUniformHandles = builder.fUniformHandles;
     const GrPipeline& pipeline = programInfo.pipeline();
     auto colorState = create_color_state(gpu, pipeline, colorFormat);
-    dawn::DepthStencilStateDescriptor depthStencilState;
-    GrStencilSettings stencil;
+    wgpu::DepthStencilStateDescriptor depthStencilState;
+
+#ifdef SK_DEBUG
     if (pipeline.isStencilEnabled()) {
-        int numStencilBits = renderTarget->renderTargetPriv().numStencilBits();
-        stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(), numStencilBits);
+        SkASSERT(renderTarget->renderTargetPriv().numStencilBits() == 8);
     }
-    depthStencilState = create_depth_stencil_state(stencil, depthStencilFormat,
-                                                   programInfo.origin());
+#endif
+    depthStencilState = create_depth_stencil_state(programInfo, depthStencilFormat);
 
-    std::vector<dawn::VertexBufferDescriptor> inputs;
+    std::vector<wgpu::VertexBufferLayoutDescriptor> inputs;
 
-    std::vector<dawn::VertexAttributeDescriptor> vertexAttributes;
+    std::vector<wgpu::VertexAttributeDescriptor> vertexAttributes;
     const GrPrimitiveProcessor& primProc = programInfo.primProc();
     if (primProc.numVertexAttributes() > 0) {
         size_t offset = 0;
         int i = 0;
         for (const auto& attrib : primProc.vertexAttributes()) {
-            dawn::VertexAttributeDescriptor attribute;
+            wgpu::VertexAttributeDescriptor attribute;
             attribute.shaderLocation = i;
             attribute.offset = offset;
             attribute.format = to_dawn_vertex_format(attrib.cpuType());
@@ -369,19 +378,19 @@
             offset += attrib.sizeAlign4();
             i++;
         }
-        dawn::VertexBufferDescriptor input;
-        input.stride = offset;
-        input.stepMode = dawn::InputStepMode::Vertex;
+        wgpu::VertexBufferLayoutDescriptor input;
+        input.arrayStride = offset;
+        input.stepMode = wgpu::InputStepMode::Vertex;
         input.attributeCount = vertexAttributes.size();
         input.attributes = &vertexAttributes.front();
         inputs.push_back(input);
     }
-    std::vector<dawn::VertexAttributeDescriptor> instanceAttributes;
+    std::vector<wgpu::VertexAttributeDescriptor> instanceAttributes;
     if (primProc.numInstanceAttributes() > 0) {
         size_t offset = 0;
         int i = 0;
         for (const auto& attrib : primProc.instanceAttributes()) {
-            dawn::VertexAttributeDescriptor attribute;
+            wgpu::VertexAttributeDescriptor attribute;
             attribute.shaderLocation = i;
             attribute.offset = offset;
             attribute.format = to_dawn_vertex_format(attrib.cpuType());
@@ -389,32 +398,32 @@
             offset += attrib.sizeAlign4();
             i++;
         }
-        dawn::VertexBufferDescriptor input;
-        input.stride = offset;
-        input.stepMode = dawn::InputStepMode::Instance;
+        wgpu::VertexBufferLayoutDescriptor input;
+        input.arrayStride = offset;
+        input.stepMode = wgpu::InputStepMode::Instance;
         input.attributeCount = instanceAttributes.size();
         input.attributes = &instanceAttributes.front();
         inputs.push_back(input);
     }
-    dawn::VertexInputDescriptor vertexInput;
-    vertexInput.indexFormat = dawn::IndexFormat::Uint16;
-    vertexInput.bufferCount = inputs.size();
-    vertexInput.buffers = &inputs.front();
+    wgpu::VertexStateDescriptor vertexState;
+    vertexState.indexFormat = wgpu::IndexFormat::Uint16;
+    vertexState.vertexBufferCount = inputs.size();
+    vertexState.vertexBuffers = &inputs.front();
 
-    dawn::ProgrammableStageDescriptor vsDesc;
+    wgpu::ProgrammableStageDescriptor vsDesc;
     vsDesc.module = vsModule;
     vsDesc.entryPoint = "main";
 
-    dawn::ProgrammableStageDescriptor fsDesc;
+    wgpu::ProgrammableStageDescriptor fsDesc;
     fsDesc.module = fsModule;
     fsDesc.entryPoint = "main";
 
-    dawn::RenderPipelineDescriptor rpDesc;
+    wgpu::RenderPipelineDescriptor rpDesc;
     rpDesc.layout = pipelineLayout;
     rpDesc.vertexStage = vsDesc;
     rpDesc.fragmentStage = &fsDesc;
-    rpDesc.vertexInput = &vertexInput;
-    rpDesc.primitiveTopology = to_dawn_primitive_topology(primitiveType);
+    rpDesc.vertexState = &vertexState;
+    rpDesc.primitiveTopology = to_dawn_primitive_topology(programInfo.primitiveType());
     if (hasDepthStencil) {
         rpDesc.depthStencilState = &depthStencilState;
     }
@@ -434,11 +443,11 @@
     , fUniformHandler(this) {
 }
 
-dawn::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBuilder& builder,
+wgpu::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBuilder& builder,
                                                             SkSL::Program::Kind kind,
                                                             bool flipY,
                                                             SkSL::Program::Inputs* inputs) {
-    dawn::Device device = fGpu->device();
+    wgpu::Device device = fGpu->device();
     SkString source(builder.fCompilerString.c_str());
 
 #if 0
@@ -446,9 +455,13 @@
     printf("converting program:\n%s\n", sksl.c_str());
 #endif
 
-    SkSL::String spirvSource = sksl_to_spirv(fGpu, source.c_str(), kind, flipY, inputs);
+    SkSL::String spirvSource = sksl_to_spirv(fGpu, source.c_str(), kind, flipY,
+                                             fUniformHandler.getRTHeightOffset(), inputs);
+    if (inputs->fRTHeight) {
+        this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
+    }
 
-    dawn::ShaderModuleDescriptor desc;
+    wgpu::ShaderModuleDescriptor desc;
     desc.codeSize = spirvSource.size() / 4;
     desc.code = reinterpret_cast<const uint32_t*>(spirvSource.c_str());
 
@@ -480,19 +493,19 @@
     }
 }
 
-static void setTexture(GrDawnGpu* gpu, const GrSamplerState& state, GrTexture* texture,
-                       std::vector<dawn::BindGroupBinding> *bindings, int* binding) {
+static void set_texture(GrDawnGpu* gpu, const GrSamplerState& state, GrTexture* texture,
+                        std::vector<wgpu::BindGroupBinding> *bindings, int* binding) {
     // FIXME: could probably cache samplers in GrDawnProgram
-    dawn::Sampler sampler = gpu->getOrCreateSampler(state);
+    wgpu::Sampler sampler = gpu->getOrCreateSampler(state);
     bindings->push_back(make_bind_group_binding((*binding)++, sampler));
     GrDawnTexture* tex = static_cast<GrDawnTexture*>(texture);
-    dawn::TextureView textureView = tex->textureView();
+    wgpu::TextureView textureView = tex->textureView();
     bindings->push_back(make_bind_group_binding((*binding)++, textureView));
 }
 
-dawn::BindGroup GrDawnProgram::setData(GrDawnGpu* gpu, const GrRenderTarget* renderTarget,
-                                       const GrProgramInfo& programInfo) {
-    std::vector<dawn::BindGroupBinding> bindings;
+wgpu::BindGroup GrDawnProgram::setUniformData(GrDawnGpu* gpu, const GrRenderTarget* renderTarget,
+                                              const GrProgramInfo& programInfo) {
+    std::vector<wgpu::BindGroupBinding> bindings;
     GrDawnRingBuffer::Slice slice;
     uint32_t uniformBufferSize = fDataManager.uniformBufferSize();
     if (0 != uniformBufferSize) {
@@ -504,40 +517,53 @@
     this->setRenderTargetState(renderTarget, programInfo.origin());
     const GrPipeline& pipeline = programInfo.pipeline();
     const GrPrimitiveProcessor& primProc = programInfo.primProc();
-    fGeometryProcessor->setData(fDataManager, primProc,
-                                GrFragmentProcessor::CoordTransformIter(pipeline));
-    int binding = GrDawnUniformHandler::kSamplerBindingBase;
-    auto primProcTextures = programInfo.hasFixedPrimProcTextures() ?
-                                programInfo.fixedPrimProcTextures() : nullptr;
-
-    for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
-        auto& sampler = primProc.textureSampler(i);
-        setTexture(gpu, sampler.samplerState(), primProcTextures[i]->peekTexture(), &bindings,
-                   &binding);
-    }
-    GrFragmentProcessor::Iter iter(pipeline);
+    GrFragmentProcessor::PipelineCoordTransformRange transformRange(pipeline);
+    fGeometryProcessor->setData(fDataManager, primProc, transformRange);
+    GrFragmentProcessor::CIter fpIter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        glslFP->setData(fDataManager, *fp);
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            auto& s = fp->textureSampler(i);
-            setTexture(gpu, s.samplerState(), s.peekTexture(), &bindings, &binding);
-        }
-        fp = iter.next();
-        glslFP = glslIter.next();
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        glslIter->setData(fDataManager, *fpIter);
     }
     SkIPoint offset;
     GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
     fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
-    if (GrTextureProxy* proxy = pipeline.dstTextureProxy()) {
-        GrFragmentProcessor::TextureSampler sampler(sk_ref_sp(proxy));
-        setTexture(gpu, sampler.samplerState(), sampler.peekTexture(), &bindings, &binding);
-    }
     fDataManager.uploadUniformBuffers(gpu, slice);
-    dawn::BindGroupDescriptor descriptor;
-    descriptor.layout = fBindGroupLayout;
+    wgpu::BindGroupDescriptor descriptor;
+    descriptor.layout = fBindGroupLayouts[0];
+    descriptor.bindingCount = bindings.size();
+    descriptor.bindings = bindings.data();
+    return gpu->device().CreateBindGroup(&descriptor);
+}
+
+wgpu::BindGroup GrDawnProgram::setTextures(GrDawnGpu* gpu,
+                                           const GrProgramInfo& programInfo,
+                                           const GrSurfaceProxy* const primProcTextures[]) {
+    std::vector<wgpu::BindGroupBinding> bindings;
+    int binding = 0;
+    const GrPipeline& pipeline = programInfo.pipeline();
+    const GrPrimitiveProcessor& primProc = programInfo.primProc();
+    if (primProcTextures) {
+        for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+            SkASSERT(primProcTextures[i]->asTextureProxy());
+            auto& sampler = primProc.textureSampler(i);
+            set_texture(gpu, sampler.samplerState(), primProcTextures[i]->peekTexture(), &bindings,
+                        &binding);
+        }
+    }
+    GrFragmentProcessor::Iter fpIter(pipeline);
+    GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        for (int i = 0; i < fpIter->numTextureSamplers(); ++i) {
+            auto& s = fpIter->textureSampler(i);
+            set_texture(gpu, s.samplerState(), s.peekTexture(), &bindings, &binding);
+        }
+    }
+    SkIPoint offset;
+    if (GrTexture* dstTexture = pipeline.peekDstTexture(&offset)) {
+        set_texture(gpu, GrSamplerState::ClampNearest(), dstTexture, &bindings, &binding);
+    }
+    wgpu::BindGroupDescriptor descriptor;
+    descriptor.layout = fBindGroupLayouts[1];
     descriptor.bindingCount = bindings.size();
     descriptor.bindings = bindings.data();
     return gpu->device().CreateBindGroup(&descriptor);
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.h b/src/gpu/dawn/GrDawnProgramBuilder.h
index ec500a0..b0758a3 100644
--- a/src/gpu/dawn/GrDawnProgramBuilder.h
+++ b/src/gpu/dawn/GrDawnProgramBuilder.h
@@ -57,26 +57,28 @@
     std::unique_ptr<GrGLSLXferProcessor> fXferProcessor;
     std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fFragmentProcessors;
     int fFragmentProcessorCnt;
-    dawn::BindGroupLayout fBindGroupLayout;
-    dawn::RenderPipeline fRenderPipeline;
+    wgpu::BindGroupLayout fBindGroupLayouts[2];
+    wgpu::RenderPipeline fRenderPipeline;
     GrDawnProgramDataManager fDataManager;
     RenderTargetState fRenderTargetState;
     BuiltinUniformHandles fBuiltinUniformHandles;
 
     void setRenderTargetState(const GrRenderTarget*, GrSurfaceOrigin);
-    dawn::BindGroup setData(GrDawnGpu* gpu, const GrRenderTarget*, const GrProgramInfo&);
+    wgpu::BindGroup setUniformData(GrDawnGpu*, const GrRenderTarget*, const GrProgramInfo&);
+    wgpu::BindGroup setTextures(GrDawnGpu* gpu,
+                                const GrProgramInfo& programInfo,
+                                const GrSurfaceProxy* const primProcTextures[]);
 };
 
 class GrDawnProgramBuilder : public GrGLSLProgramBuilder {
 public:
     static sk_sp<GrDawnProgram> Build(GrDawnGpu*,
-                                      GrRenderTarget* rt,
-                                      const GrProgramInfo& programInfo,
-                                      GrPrimitiveType primitiveType,
-                                      dawn::TextureFormat colorFormat,
+                                      GrRenderTarget*,
+                                      const GrProgramInfo&,
+                                      wgpu::TextureFormat colorFormat,
                                       bool hasDepthStencil,
-                                      dawn::TextureFormat depthStencilFormat,
-                                      GrProgramDesc* desc);
+                                      wgpu::TextureFormat depthStencilFormat,
+                                      GrProgramDesc*);
     const GrCaps* caps() const override;
     GrGLSLUniformHandler* uniformHandler() override { return &fUniformHandler; }
     const GrGLSLUniformHandler* uniformHandler() const override { return &fUniformHandler; }
@@ -89,7 +91,7 @@
                          GrRenderTarget*,
                          const GrProgramInfo&,
                          GrProgramDesc*);
-    dawn::ShaderModule createShaderModule(const GrGLSLShaderBuilder&, SkSL::Program::Kind,
+    wgpu::ShaderModule createShaderModule(const GrGLSLShaderBuilder&, SkSL::Program::Kind,
                                           bool flipY, SkSL::Program::Inputs* inputs);
     GrDawnGpu*             fGpu;
     GrDawnVaryingHandler   fVaryingHandler;
diff --git a/src/gpu/dawn/GrDawnProgramDataManager.cpp b/src/gpu/dawn/GrDawnProgramDataManager.cpp
index bf2bacc..c740edc 100644
--- a/src/gpu/dawn/GrDawnProgramDataManager.cpp
+++ b/src/gpu/dawn/GrDawnProgramDataManager.cpp
@@ -25,7 +25,7 @@
         SkDEBUGCODE(
             uniform.fArrayCount = uniformInfo.fVar.getArrayCount();
             uniform.fType = uniformInfo.fVar.getType();
-        );
+        )
         uniform.fOffset = uniformInfo.fUBOOffset;
     }
 }
diff --git a/src/gpu/dawn/GrDawnRenderTarget.h b/src/gpu/dawn/GrDawnRenderTarget.h
index 0af2a49..f93d498 100644
--- a/src/gpu/dawn/GrDawnRenderTarget.h
+++ b/src/gpu/dawn/GrDawnRenderTarget.h
@@ -27,7 +27,7 @@
 
     GrBackendRenderTarget getBackendRenderTarget() const override;
     GrBackendFormat backendFormat() const override;
-    dawn::Texture texture() const { return fInfo.fTexture; }
+    wgpu::Texture texture() const { return fInfo.fTexture; }
 
 protected:
     GrDawnRenderTarget(GrDawnGpu* gpu,
diff --git a/src/gpu/dawn/GrDawnRingBuffer.cpp b/src/gpu/dawn/GrDawnRingBuffer.cpp
index caf0d57..74ea72f 100644
--- a/src/gpu/dawn/GrDawnRingBuffer.cpp
+++ b/src/gpu/dawn/GrDawnRingBuffer.cpp
@@ -14,7 +14,7 @@
     const int kDefaultSize = 512 * 1024;
 }
 
-GrDawnRingBuffer::GrDawnRingBuffer(GrDawnGpu* gpu, dawn::BufferUsage usage)
+GrDawnRingBuffer::GrDawnRingBuffer(GrDawnGpu* gpu, wgpu::BufferUsage usage)
     : fGpu(gpu) , fUsage(usage) {
 }
 
@@ -23,8 +23,8 @@
 
 GrDawnRingBuffer::Slice GrDawnRingBuffer::allocate(int size) {
     if (!fBuffer || fOffset + size > kDefaultSize) {
-        dawn::BufferDescriptor desc;
-        desc.usage = fUsage | dawn::BufferUsage::CopyDst;
+        wgpu::BufferDescriptor desc;
+        desc.usage = fUsage | wgpu::BufferUsage::CopyDst;
         desc.size = kDefaultSize;
         fBuffer = fGpu->device().CreateBuffer(&desc);
         fOffset = 0;
diff --git a/src/gpu/dawn/GrDawnRingBuffer.h b/src/gpu/dawn/GrDawnRingBuffer.h
index 0dbe505..9b66f6f 100644
--- a/src/gpu/dawn/GrDawnRingBuffer.h
+++ b/src/gpu/dawn/GrDawnRingBuffer.h
@@ -16,11 +16,11 @@
 
 class GrDawnRingBuffer : public SkRefCnt {
 public:
-    GrDawnRingBuffer(GrDawnGpu* gpu, dawn::BufferUsage usage);
+    GrDawnRingBuffer(GrDawnGpu* gpu, wgpu::BufferUsage usage);
     ~GrDawnRingBuffer() override;
 
     struct Slice {
-        Slice(dawn::Buffer buffer, int offset) : fBuffer(buffer), fOffset(offset) {}
+        Slice(wgpu::Buffer buffer, int offset) : fBuffer(buffer), fOffset(offset) {}
         Slice() : fBuffer(nullptr), fOffset(0) {}
         Slice(const Slice& other) : fBuffer(other.fBuffer), fOffset(other.fOffset) {}
         Slice& operator=(const Slice& other) {
@@ -28,15 +28,15 @@
             fOffset = other.fOffset;
             return *this;
         }
-        dawn::Buffer fBuffer;
+        wgpu::Buffer fBuffer;
         int fOffset;
     };
     Slice allocate(int size);
 
 private:
     GrDawnGpu*            fGpu;
-    dawn::BufferUsage     fUsage;
-    dawn::Buffer          fBuffer;
+    wgpu::BufferUsage     fUsage;
+    wgpu::Buffer          fBuffer;
     int                   fOffset = 0;
 };
 
diff --git a/src/gpu/dawn/GrDawnStagingManager.cpp b/src/gpu/dawn/GrDawnStagingManager.cpp
index 68e0e97..51e11a4 100644
--- a/src/gpu/dawn/GrDawnStagingManager.cpp
+++ b/src/gpu/dawn/GrDawnStagingManager.cpp
@@ -9,7 +9,7 @@
 
 #include "src/core/SkMathPriv.h"
 
-GrDawnStagingManager::GrDawnStagingManager(dawn::Device device) : fDevice(device) {
+GrDawnStagingManager::GrDawnStagingManager(wgpu::Device device) : fDevice(device) {
 }
 
 GrDawnStagingManager::~GrDawnStagingManager() {
@@ -27,10 +27,10 @@
         stagingBuffer = i->second;
         fReadyPool.erase(i);
     } else {
-        dawn::BufferDescriptor desc;
-        desc.usage = dawn::BufferUsage::MapWrite | dawn::BufferUsage::CopySrc;
+        wgpu::BufferDescriptor desc;
+        desc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
         desc.size = sizePow2;
-        dawn::CreateBufferMappedResult result = fDevice.CreateBufferMapped(&desc);
+        wgpu::CreateBufferMappedResult result = fDevice.CreateBufferMapped(&desc);
         std::unique_ptr<GrDawnStagingBuffer> b(new GrDawnStagingBuffer(
             this, result.buffer, sizePow2, result.data));
         stagingBuffer = b.get();
diff --git a/src/gpu/dawn/GrDawnStagingManager.h b/src/gpu/dawn/GrDawnStagingManager.h
index b3974ad..32a1081 100644
--- a/src/gpu/dawn/GrDawnStagingManager.h
+++ b/src/gpu/dawn/GrDawnStagingManager.h
@@ -18,7 +18,7 @@
 
 class GrDawnStagingManager {
 public:
-    GrDawnStagingManager(dawn::Device device);
+    GrDawnStagingManager(wgpu::Device device);
    ~GrDawnStagingManager();
     GrDawnStagingBuffer* findOrCreateStagingBuffer(size_t size);
 
@@ -26,7 +26,7 @@
     void mapBusyList();
 
 private:
-    dawn::Device                                       fDevice;
+    wgpu::Device                                       fDevice;
     std::vector<std::unique_ptr<GrDawnStagingBuffer>>  fBuffers;
     std::multimap<size_t, GrDawnStagingBuffer*>        fReadyPool;
     std::vector<GrDawnStagingBuffer*>                  fBusyList;
@@ -34,14 +34,14 @@
 };
 
 struct GrDawnStagingBuffer {
-    GrDawnStagingBuffer(GrDawnStagingManager* manager, dawn::Buffer buffer, size_t size,
+    GrDawnStagingBuffer(GrDawnStagingManager* manager, wgpu::Buffer buffer, size_t size,
                        void* data)
         : fManager(manager), fBuffer(buffer), fSize(size), fData(data) {}
     ~GrDawnStagingBuffer() {
         fManager = nullptr;
     }
     GrDawnStagingManager*  fManager;
-    dawn::Buffer           fBuffer;
+    wgpu::Buffer           fBuffer;
     size_t                 fSize;
     void*                  fData;
 };
diff --git a/src/gpu/dawn/GrDawnStencilAttachment.cpp b/src/gpu/dawn/GrDawnStencilAttachment.cpp
index 7e4047a..b96107b 100644
--- a/src/gpu/dawn/GrDawnStencilAttachment.cpp
+++ b/src/gpu/dawn/GrDawnStencilAttachment.cpp
@@ -17,8 +17,8 @@
                                                  int height,
                                                  int bits,
                                                  int samples,
-                                                 dawn::Texture texture,
-                                                 dawn::TextureView view)
+                                                 wgpu::Texture texture,
+                                                 wgpu::TextureView view)
     : INHERITED(gpu, width, height, bits, samples)
     , fTexture(texture)
     , fView(view) {
@@ -29,17 +29,17 @@
                                                          int width,
                                                          int height,
                                                          int sampleCnt) {
-    dawn::TextureDescriptor desc;
-    desc.usage = dawn::TextureUsage::OutputAttachment;
+    wgpu::TextureDescriptor desc;
+    desc.usage = wgpu::TextureUsage::OutputAttachment;
     desc.size.width = width;
     desc.size.height = height;
     desc.size.depth = 1;
-    desc.format = dawn::TextureFormat::Depth24PlusStencil8;
-    dawn::Texture texture = gpu->device().CreateTexture(&desc);
+    desc.format = wgpu::TextureFormat::Depth24PlusStencil8;
+    wgpu::Texture texture = gpu->device().CreateTexture(&desc);
     if (!texture) {
         return nullptr;
     }
-    dawn::TextureView view = texture.CreateView();
+    wgpu::TextureView view = texture.CreateView();
     if (!view) {
         return nullptr;
     }
diff --git a/src/gpu/dawn/GrDawnStencilAttachment.h b/src/gpu/dawn/GrDawnStencilAttachment.h
index b0afc23..0e4a891 100644
--- a/src/gpu/dawn/GrDawnStencilAttachment.h
+++ b/src/gpu/dawn/GrDawnStencilAttachment.h
@@ -20,7 +20,7 @@
                                            int sampleCnt);
 
     ~GrDawnStencilAttachment() override;
-    dawn::TextureView view() const { return fView; }
+    wgpu::TextureView view() const { return fView; }
 
 protected:
     void onRelease() override;
@@ -30,12 +30,12 @@
     size_t onGpuMemorySize() const override;
 
     GrDawnStencilAttachment(GrDawnGpu* gpu, int width, int height, int bits, int samples,
-                            dawn::Texture texture, dawn::TextureView view);
+                            wgpu::Texture texture, wgpu::TextureView view);
 
     GrDawnGpu* getDawnGpu() const;
 
-    dawn::Texture fTexture;
-    dawn::TextureView fView;
+    wgpu::Texture fTexture;
+    wgpu::TextureView fView;
 
     typedef GrStencilAttachment INHERITED;
 };
diff --git a/src/gpu/dawn/GrDawnTexture.cpp b/src/gpu/dawn/GrDawnTexture.cpp
index 795f439..9735ffd 100644
--- a/src/gpu/dawn/GrDawnTexture.cpp
+++ b/src/gpu/dawn/GrDawnTexture.cpp
@@ -15,7 +15,7 @@
 GrDawnTexture::GrDawnTexture(GrDawnGpu* gpu,
                              const SkISize& dimensions,
                              GrPixelConfig config,
-                             dawn::TextureView textureView,
+                             wgpu::TextureView textureView,
                              const GrDawnImageInfo& info,
                              GrMipMapsStatus mipMapsStatus)
         : GrSurface(gpu, dimensions, config, GrProtected::kNo)
@@ -24,20 +24,20 @@
         , fTextureView(textureView) {}
 
 sk_sp<GrDawnTexture> GrDawnTexture::Make(GrDawnGpu* gpu, const SkISize& dimensions,
-                                         GrPixelConfig config, dawn::TextureFormat format,
+                                         GrPixelConfig config, wgpu::TextureFormat format,
                                          GrRenderable renderable, int sampleCnt,
                                          SkBudgeted budgeted, int mipLevels,
                                          GrMipMapsStatus status) {
     bool renderTarget = renderable == GrRenderable::kYes;
-    dawn::TextureDescriptor textureDesc;
+    wgpu::TextureDescriptor textureDesc;
 
     textureDesc.usage =
-        dawn::TextureUsage::Sampled |
-        dawn::TextureUsage::CopySrc |
-        dawn::TextureUsage::CopyDst;
+        wgpu::TextureUsage::Sampled |
+        wgpu::TextureUsage::CopySrc |
+        wgpu::TextureUsage::CopyDst;
 
     if (renderTarget) {
-        textureDesc.usage |= dawn::TextureUsage::OutputAttachment;
+        textureDesc.usage |= wgpu::TextureUsage::OutputAttachment;
     }
 
     textureDesc.size.width = dimensions.fWidth;
@@ -47,13 +47,13 @@
     textureDesc.mipLevelCount = std::max(mipLevels, 1);
     textureDesc.sampleCount = sampleCnt;
 
-    dawn::Texture tex = gpu->device().CreateTexture(&textureDesc);
+    wgpu::Texture tex = gpu->device().CreateTexture(&textureDesc);
 
     if (!tex) {
         return nullptr;
     }
 
-    dawn::TextureView textureView = tex.CreateView();
+    wgpu::TextureView textureView = tex.CreateView();
 
     if (!textureView) {
         return nullptr;
@@ -89,7 +89,7 @@
                                                 int sampleCnt, GrMipMapsStatus status,
                                                 GrWrapCacheable cacheable,
                                                 const GrDawnImageInfo& info) {
-    dawn::TextureView textureView = info.fTexture.CreateView();
+    wgpu::TextureView textureView = info.fTexture.CreateView();
     if (!textureView) {
         return nullptr;
     }
@@ -127,13 +127,13 @@
 }
 
 void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels,
-                           dawn::CommandEncoder copyEncoder) {
+                           wgpu::CommandEncoder copyEncoder) {
     this->upload(texels, mipLevels, SkIRect::MakeWH(width(), height()), copyEncoder);
 }
 
 void GrDawnTexture::upload(const GrMipLevel texels[], int mipLevels, const SkIRect& rect,
-                           dawn::CommandEncoder copyEncoder) {
-    dawn::Device device = this->getDawnGpu()->device();
+                           wgpu::CommandEncoder copyEncoder) {
+    wgpu::Device device = this->getDawnGpu()->device();
 
     uint32_t x = rect.x();
     uint32_t y = rect.y();
@@ -149,26 +149,26 @@
         size_t size = dstRowBytes * height;
         GrDawnStagingBuffer* stagingBuffer = getDawnGpu()->getStagingBuffer(size);
         SkRectMemcpy(stagingBuffer->fData, dstRowBytes, src, srcRowBytes, trimRowBytes, height);
-        dawn::Buffer buffer = stagingBuffer->fBuffer;
+        wgpu::Buffer buffer = stagingBuffer->fBuffer;
         buffer.Unmap();
         stagingBuffer->fData = nullptr;
 
-        dawn::BufferCopyView srcBuffer;
+        wgpu::BufferCopyView srcBuffer;
         srcBuffer.buffer = buffer;
         srcBuffer.offset = 0;
         srcBuffer.rowPitch = dstRowBytes;
         srcBuffer.imageHeight = height;
 
-        dawn::TextureCopyView dstTexture;
+        wgpu::TextureCopyView dstTexture;
         dstTexture.texture = fInfo.fTexture;
         dstTexture.mipLevel = i;
         dstTexture.origin = {x, y, 0};
 
-        dawn::Extent3D copySize = {width, height, 1};
+        wgpu::Extent3D copySize = {width, height, 1};
         copyEncoder.CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
         x /= 2;
         y /= 2;
-        width /= 2;
-        height /= 2;
+        width = SkTMax(1u, width / 2);
+        height = SkTMax(1u, height / 2);
     }
 }
diff --git a/src/gpu/dawn/GrDawnTexture.h b/src/gpu/dawn/GrDawnTexture.h
index bbea6f8..5d25d8b 100644
--- a/src/gpu/dawn/GrDawnTexture.h
+++ b/src/gpu/dawn/GrDawnTexture.h
@@ -17,7 +17,7 @@
 class GrDawnTexture : public GrTexture {
 public:
     static sk_sp<GrDawnTexture> Make(GrDawnGpu*, const SkISize& dimensions, GrPixelConfig config,
-                                     dawn::TextureFormat format, GrRenderable, int sampleCnt,
+                                     wgpu::TextureFormat format, GrRenderable, int sampleCnt,
                                      SkBudgeted, int mipLevels, GrMipMapsStatus);
 
     static sk_sp<GrDawnTexture> MakeWrapped(GrDawnGpu*, const SkISize& dimensions,
@@ -32,14 +32,14 @@
 
     void textureParamsModified() override {}
 
-    void upload(const GrMipLevel texels[], int mipLevels, dawn::CommandEncoder copyEncoder);
+    void upload(const GrMipLevel texels[], int mipLevels, wgpu::CommandEncoder copyEncoder);
     void upload(const GrMipLevel texels[], int mipLevels, const SkIRect& dstRect,
-                dawn::CommandEncoder copyEncoder);
+                wgpu::CommandEncoder copyEncoder);
 
-    dawn::Texture texture() const { return fInfo.fTexture; }
-    dawn::TextureView textureView() const { return fTextureView; }
+    wgpu::Texture texture() const { return fInfo.fTexture; }
+    wgpu::TextureView textureView() const { return fTextureView; }
 protected:
-    GrDawnTexture(GrDawnGpu*, const SkISize& dimensions, GrPixelConfig config, dawn::TextureView,
+    GrDawnTexture(GrDawnGpu*, const SkISize& dimensions, GrPixelConfig config, wgpu::TextureView,
                   const GrDawnImageInfo&, GrMipMapsStatus);
 
     GrDawnGpu* getDawnGpu() const;
@@ -55,7 +55,7 @@
     GrDawnTexture(GrDawnGpu*, const GrSurfaceDesc&, const GrDawnImageInfo&, GrMipMapsStatus);
 
     GrDawnImageInfo          fInfo;
-    dawn::TextureView        fTextureView;
+    wgpu::TextureView        fTextureView;
 
     typedef GrTexture INHERITED;
 };
diff --git a/src/gpu/dawn/GrDawnTextureRenderTarget.cpp b/src/gpu/dawn/GrDawnTextureRenderTarget.cpp
index e453379..d94fd24 100644
--- a/src/gpu/dawn/GrDawnTextureRenderTarget.cpp
+++ b/src/gpu/dawn/GrDawnTextureRenderTarget.cpp
@@ -15,7 +15,7 @@
 GrDawnTextureRenderTarget::GrDawnTextureRenderTarget(GrDawnGpu* gpu,
                                                      const SkISize& dimensions,
                                                      GrPixelConfig config,
-                                                     dawn::TextureView textureView,
+                                                     wgpu::TextureView textureView,
                                                      int sampleCnt,
                                                      const GrDawnImageInfo& info,
                                                      GrMipMapsStatus mipMapsStatus)
diff --git a/src/gpu/dawn/GrDawnTextureRenderTarget.h b/src/gpu/dawn/GrDawnTextureRenderTarget.h
index 73dbcc5..48a1bfa 100644
--- a/src/gpu/dawn/GrDawnTextureRenderTarget.h
+++ b/src/gpu/dawn/GrDawnTextureRenderTarget.h
@@ -24,7 +24,7 @@
     GrDawnTextureRenderTarget(GrDawnGpu* gpu,
                               const SkISize& dimensions,
                               GrPixelConfig config,
-                              const dawn::TextureView textureView,
+                              const wgpu::TextureView textureView,
                               int sampleCnt,
                               const GrDawnImageInfo& info,
                               GrMipMapsStatus mipMapsStatus);
diff --git a/src/gpu/dawn/GrDawnUniformHandler.cpp b/src/gpu/dawn/GrDawnUniformHandler.cpp
index c877096..e8506ae 100644
--- a/src/gpu/dawn/GrDawnUniformHandler.cpp
+++ b/src/gpu/dawn/GrDawnUniformHandler.cpp
@@ -175,9 +175,7 @@
     SK_ABORT("Unexpected type");
 }
 
-uint32_t get_ubo_offset(uint32_t* currentOffset,
-                        GrSLType type,
-                        int arrayCount) {
+uint32_t get_ubo_offset(uint32_t* currentOffset, GrSLType type, int arrayCount) {
     uint32_t alignmentMask = grsltype_to_alignment_mask(type);
     // We want to use the std140 layout here, so we must make arrays align to 16 bytes.
     if (arrayCount || type == kFloat2x2_GrSLType) {
@@ -233,7 +231,7 @@
     fUniforms[u.toIndex()].fVisibility |= visibility;
 }
 
-GrGLSLUniformHandler::SamplerHandle GrDawnUniformHandler::addSampler(const GrTextureProxy*,
+GrGLSLUniformHandler::SamplerHandle GrDawnUniformHandler::addSampler(const GrSurfaceProxy*,
                                                                      const GrSamplerState&,
                                                                      const GrSwizzle& swizzle,
                                                                      const char* name,
@@ -243,13 +241,13 @@
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
     GrSLType samplerType = kSampler_GrSLType, textureType = kTexture2D_GrSLType;
-    int binding = kSamplerBindingBase + fSamplers.count() * 2;
+    int binding = fSamplers.count() * 2;
     UniformInfo& info = fSamplers.push_back();
     info.fVar.setType(samplerType);
     info.fVar.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
     info.fVar.setName(mangleName);
     SkString layoutQualifier;
-    layoutQualifier.appendf("set = 0, binding = %d", binding);
+    layoutQualifier.appendf("set = 1, binding = %d", binding);
     info.fVar.addLayoutQualifier(layoutQualifier.c_str());
     info.fVisibility = kFragment_GrShaderFlag;
     info.fUBOOffset = 0;
@@ -264,7 +262,7 @@
     texInfo.fVar.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
     texInfo.fVar.setName(mangleTexName);
     SkString texLayoutQualifier;
-    texLayoutQualifier.appendf("set = 0, binding = %d", binding + 1);
+    texLayoutQualifier.appendf("set = 1, binding = %d", binding + 1);
     texInfo.fVar.addLayoutQualifier(texLayoutQualifier.c_str());
     texInfo.fVisibility = kFragment_GrShaderFlag;
     texInfo.fUBOOffset = 0;
@@ -306,3 +304,8 @@
         out->appendf("%s\n};\n", uniformsString.c_str());
     }
 }
+
+uint32_t GrDawnUniformHandler::getRTHeightOffset() const {
+    uint32_t dummy = fCurrentUBOOffset;
+    return get_ubo_offset(&dummy, kFloat_GrSLType, 0);
+}
diff --git a/src/gpu/dawn/GrDawnUniformHandler.h b/src/gpu/dawn/GrDawnUniformHandler.h
index 218eec2..ba653a4 100644
--- a/src/gpu/dawn/GrDawnUniformHandler.h
+++ b/src/gpu/dawn/GrDawnUniformHandler.h
@@ -28,13 +28,13 @@
     typedef GrTAllocator<UniformInfo> UniformInfoArray;
     enum {
         kUniformBinding = 0,
-        kSamplerBindingBase = 1,
     };
+    uint32_t getRTHeightOffset() const;
 
 private:
     explicit GrDawnUniformHandler(GrGLSLProgramBuilder* program);
 
-    SamplerHandle addSampler(const GrTextureProxy*, const GrSamplerState&, const GrSwizzle&,
+    SamplerHandle addSampler(const GrSurfaceProxy*, const GrSamplerState&, const GrSwizzle&,
                              const char* name, const GrShaderCaps*) override;
     const char* samplerVariable(SamplerHandle handle) const override;
     GrSwizzle samplerSwizzle(SamplerHandle handle) const override;
@@ -55,6 +55,7 @@
     SkTArray<SkString>   fSamplerReferences;
 
     uint32_t fCurrentUBOOffset = 0;
+    uint32_t fRTHeightOffset = 0;
 
     friend class GrDawnProgramBuilder;
     typedef GrGLSLUniformHandler INHERITED;
diff --git a/src/gpu/dawn/GrDawnUtil.cpp b/src/gpu/dawn/GrDawnUtil.cpp
index 7404dac..9ec9047 100644
--- a/src/gpu/dawn/GrDawnUtil.cpp
+++ b/src/gpu/dawn/GrDawnUtil.cpp
@@ -7,14 +7,14 @@
 
 #include "src/gpu/dawn/GrDawnUtil.h"
 
-size_t GrDawnBytesPerPixel(dawn::TextureFormat format) {
+size_t GrDawnBytesPerPixel(wgpu::TextureFormat format) {
     switch (format) {
-        case dawn::TextureFormat::RGBA8Unorm:
-        case dawn::TextureFormat::BGRA8Unorm:
+        case wgpu::TextureFormat::RGBA8Unorm:
+        case wgpu::TextureFormat::BGRA8Unorm:
             return 4;
-        case dawn::TextureFormat::R8Unorm:
+        case wgpu::TextureFormat::R8Unorm:
             return 1;
-        case dawn::TextureFormat::Depth24PlusStencil8:
+        case wgpu::TextureFormat::Depth24PlusStencil8:
             return 4;
         default:
             SkASSERT(false);
@@ -22,26 +22,26 @@
     }
 }
 
-bool GrDawnFormatIsRenderable(dawn::TextureFormat format) {
+bool GrDawnFormatIsRenderable(wgpu::TextureFormat format) {
     // For now, all the formats above are renderable. If a non-renderable format is added
     // (see dawn/src/dawn_native/Format.cpp), an exception should be added here.
     return true;
 }
 
-bool GrPixelConfigToDawnFormat(GrPixelConfig config, dawn::TextureFormat* format) {
+bool GrPixelConfigToDawnFormat(GrPixelConfig config, wgpu::TextureFormat* format) {
     switch (config) {
         case kRGBA_8888_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
         case kRGB_565_GrPixelConfig:
         case kGray_8_GrPixelConfig:
-            *format = dawn::TextureFormat::RGBA8Unorm;
+            *format = wgpu::TextureFormat::RGBA8Unorm;
             return true;
         case kBGRA_8888_GrPixelConfig:
-            *format = dawn::TextureFormat::BGRA8Unorm;
+            *format = wgpu::TextureFormat::BGRA8Unorm;
             return true;
         case kAlpha_8_GrPixelConfig:
         case kAlpha_8_as_Red_GrPixelConfig:
-            *format = dawn::TextureFormat::R8Unorm;
+            *format = wgpu::TextureFormat::R8Unorm;
             return true;
         default:
             return false;
@@ -54,15 +54,15 @@
 }
 
 #if GR_TEST_UTILS
-const char* GrDawnFormatToStr(dawn::TextureFormat format) {
+const char* GrDawnFormatToStr(wgpu::TextureFormat format) {
     switch (format) {
-        case dawn::TextureFormat::RGBA8Unorm:
+        case wgpu::TextureFormat::RGBA8Unorm:
             return "RGBA8Unorm";
-        case dawn::TextureFormat::BGRA8Unorm:
+        case wgpu::TextureFormat::BGRA8Unorm:
             return "BGRA8Unorm";
-        case dawn::TextureFormat::R8Unorm:
+        case wgpu::TextureFormat::R8Unorm:
             return "R8Unorm";
-        case dawn::TextureFormat::Depth24PlusStencil8:
+        case wgpu::TextureFormat::Depth24PlusStencil8:
             return "Depth24PlusStencil8";
         default:
             SkASSERT(false);
diff --git a/src/gpu/dawn/GrDawnUtil.h b/src/gpu/dawn/GrDawnUtil.h
index 0e5a41b..4592073 100644
--- a/src/gpu/dawn/GrDawnUtil.h
+++ b/src/gpu/dawn/GrDawnUtil.h
@@ -11,12 +11,12 @@
 #include "include/private/GrTypesPriv.h"
 #include "dawn/dawncpp.h"
 
-size_t GrDawnBytesPerPixel(dawn::TextureFormat format);
-bool GrDawnFormatIsRenderable(dawn::TextureFormat format);
-bool GrPixelConfigToDawnFormat(GrPixelConfig config, dawn::TextureFormat* format);
+size_t GrDawnBytesPerPixel(wgpu::TextureFormat format);
+bool GrDawnFormatIsRenderable(wgpu::TextureFormat format);
+bool GrPixelConfigToDawnFormat(GrPixelConfig config, wgpu::TextureFormat* format);
 size_t GrDawnRoundRowBytes(size_t rowBytes);
 #if GR_TEST_UTILS
-const char* GrDawnFormatToStr(dawn::TextureFormat format);
+const char* GrDawnFormatToStr(wgpu::TextureFormat format);
 #endif
 
 #endif // GrDawnUtil_DEFINED
diff --git a/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp b/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp
index cab332b..d9d1933 100644
--- a/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp
+++ b/src/gpu/effects/GrAlphaThresholdFragmentProcessor.fp
@@ -18,7 +18,7 @@
 }
 
 @make {
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> mask,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> mask,
                                                      float innerThreshold,
                                                      float outerThreshold,
                                                      const SkIRect& bounds) {
diff --git a/src/gpu/effects/GrAtlasedShaderHelpers.h b/src/gpu/effects/GrAtlasedShaderHelpers.h
index cf615b3..81bc5c5 100644
--- a/src/gpu/effects/GrAtlasedShaderHelpers.h
+++ b/src/gpu/effects/GrAtlasedShaderHelpers.h
@@ -59,6 +59,13 @@
                                        const GrGLSLVarying &texIdx,
                                        const char* coordName,
                                        const char* colorName) {
+    SkASSERT(numTextureSamplers > 0);
+    // This shouldn't happen, but will avoid a crash if it does
+    if (numTextureSamplers <= 0) {
+        args.fFragBuilder->codeAppendf("%s = float4(1, 1, 1, 1);", colorName);
+        return;
+    }
+
     // conditionally load from the indexed texture sampler
     for (int i = 0; i < numTextureSamplers-1; ++i) {
         args.fFragBuilder->codeAppendf("if (%s == %d) { %s = ", texIdx.fsIn(), i, colorName);
diff --git a/src/gpu/effects/GrBezierEffect.cpp b/src/gpu/effects/GrBezierEffect.cpp
index 7c9e5f0..9066c40 100644
--- a/src/gpu/effects/GrBezierEffect.cpp
+++ b/src/gpu/effects/GrBezierEffect.cpp
@@ -26,7 +26,7 @@
                               GrProcessorKeyBuilder*);
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const GrConicEffect& ce = primProc.cast<GrConicEffect>();
 
         if (!ce.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(ce.viewMatrix())) {
@@ -45,7 +45,7 @@
             pdman.set1f(fCoverageScaleUniform, GrNormalizeByteToFloat(ce.coverageScale()));
             fCoverageScale = ce.coverageScale();
         }
-        this->setTransformDataHelper(ce.localMatrix(), pdman, &transformIter);
+        this->setTransformDataHelper(ce.localMatrix(), pdman, transformRange);
     }
 
 private:
@@ -233,13 +233,13 @@
 GrConicEffect::GrConicEffect(const SkPMColor4f& color, const SkMatrix& viewMatrix, uint8_t coverage,
                              GrClipEdgeType edgeType, const SkMatrix& localMatrix,
                              bool usesLocalCoords)
-    : INHERITED(kGrConicEffect_ClassID)
-    , fColor(color)
-    , fViewMatrix(viewMatrix)
-    , fLocalMatrix(viewMatrix)
-    , fUsesLocalCoords(usesLocalCoords)
-    , fCoverageScale(coverage)
-    , fEdgeType(edgeType) {
+        : INHERITED(kGrConicEffect_ClassID)
+        , fColor(color)
+        , fViewMatrix(viewMatrix)
+        , fLocalMatrix(viewMatrix)
+        , fUsesLocalCoords(usesLocalCoords)
+        , fCoverageScale(coverage)
+        , fEdgeType(edgeType) {
     this->setVertexAttributes(kAttributes, SK_ARRAY_COUNT(kAttributes));
 }
 
@@ -248,13 +248,14 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrConicEffect);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrConicEffect::TestCreate(GrProcessorTestData* d) {
-    sk_sp<GrGeometryProcessor> gp;
+GrGeometryProcessor* GrConicEffect::TestCreate(GrProcessorTestData* d) {
+    GrGeometryProcessor* gp;
     do {
         GrClipEdgeType edgeType =
                 static_cast<GrClipEdgeType>(
                         d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
-        gp = GrConicEffect::Make(SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
+        gp = GrConicEffect::Make(d->allocator(),
+                                 SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                  GrTest::TestMatrix(d->fRandom), edgeType, *d->caps(),
                                  GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool());
     } while (nullptr == gp);
@@ -277,7 +278,7 @@
                               GrProcessorKeyBuilder*);
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const GrQuadEffect& qe = primProc.cast<GrQuadEffect>();
 
         if (!qe.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(qe.viewMatrix())) {
@@ -296,7 +297,7 @@
             pdman.set1f(fCoverageScaleUniform, GrNormalizeByteToFloat(qe.coverageScale()));
             fCoverageScale = qe.coverageScale();
         }
-        this->setTransformDataHelper(qe.localMatrix(), pdman, &transformIter);
+        this->setTransformDataHelper(qe.localMatrix(), pdman, transformRange);
     }
 
 private:
@@ -448,12 +449,13 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrQuadEffect);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrQuadEffect::TestCreate(GrProcessorTestData* d) {
-    sk_sp<GrGeometryProcessor> gp;
+GrGeometryProcessor* GrQuadEffect::TestCreate(GrProcessorTestData* d) {
+    GrGeometryProcessor* gp;
     do {
         GrClipEdgeType edgeType = static_cast<GrClipEdgeType>(
                 d->fRandom->nextULessThan(kGrClipEdgeTypeCnt));
-        gp = GrQuadEffect::Make(SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
+        gp = GrQuadEffect::Make(d->allocator(),
+                                SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                 GrTest::TestMatrix(d->fRandom), edgeType, *d->caps(),
                                 GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool());
     } while (nullptr == gp);
diff --git a/src/gpu/effects/GrBezierEffect.h b/src/gpu/effects/GrBezierEffect.h
index c63002b..c59faeb 100644
--- a/src/gpu/effects/GrBezierEffect.h
+++ b/src/gpu/effects/GrBezierEffect.h
@@ -9,6 +9,7 @@
 #define GrBezierEffect_DEFINED
 
 #include "include/private/GrTypesPriv.h"
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrProcessor.h"
@@ -57,36 +58,29 @@
 
 class GrConicEffect : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(const SkPMColor4f& color,
-                                           const SkMatrix& viewMatrix,
-                                           const GrClipEdgeType edgeType,
-                                           const GrCaps& caps,
-                                           const SkMatrix& localMatrix,
-                                           bool usesLocalCoords,
-                                           uint8_t coverage = 0xff) {
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const SkPMColor4f& color,
+                                     const SkMatrix& viewMatrix,
+                                     const GrClipEdgeType edgeType,
+                                     const GrCaps& caps,
+                                     const SkMatrix& localMatrix,
+                                     bool usesLocalCoords,
+                                     uint8_t coverage = 0xff) {
         switch (edgeType) {
-            case GrClipEdgeType::kFillAA:
-                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
-                    return nullptr;
-                }
-                return sk_sp<GrGeometryProcessor>(
-                    new GrConicEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillAA,
-                                      localMatrix, usesLocalCoords));
+            case GrClipEdgeType::kFillAA:       // fall through
             case GrClipEdgeType::kHairlineAA:
                 if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return nullptr;
                 }
-                return sk_sp<GrGeometryProcessor>(
-                    new GrConicEffect(color, viewMatrix, coverage,
-                                      GrClipEdgeType::kHairlineAA, localMatrix,
-                                      usesLocalCoords));
+                break;
             case GrClipEdgeType::kFillBW:
-                return sk_sp<GrGeometryProcessor>(
-                    new GrConicEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillBW,
-                                      localMatrix, usesLocalCoords));
-            default:
+                break;
+            default: // kInverseFillBW or kInverseFillAA
                 return nullptr;
         }
+
+        return arena->make<GrConicEffect>(color, viewMatrix, coverage, edgeType, localMatrix,
+                                          usesLocalCoords);
     }
 
     ~GrConicEffect() override;
@@ -109,6 +103,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrConicEffect(const SkPMColor4f&, const SkMatrix& viewMatrix, uint8_t coverage, GrClipEdgeType,
                   const SkMatrix& localMatrix, bool usesLocalCoords);
 
@@ -141,36 +137,29 @@
 
 class GrQuadEffect : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(const SkPMColor4f& color,
-                                           const SkMatrix& viewMatrix,
-                                           const GrClipEdgeType edgeType,
-                                           const GrCaps& caps,
-                                           const SkMatrix& localMatrix,
-                                           bool usesLocalCoords,
-                                           uint8_t coverage = 0xff) {
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const SkPMColor4f& color,
+                                     const SkMatrix& viewMatrix,
+                                     const GrClipEdgeType edgeType,
+                                     const GrCaps& caps,
+                                     const SkMatrix& localMatrix,
+                                     bool usesLocalCoords,
+                                     uint8_t coverage = 0xff) {
         switch (edgeType) {
-            case GrClipEdgeType::kFillAA:
-                if (!caps.shaderCaps()->shaderDerivativeSupport()) {
-                    return nullptr;
-                }
-                return sk_sp<GrGeometryProcessor>(
-                    new GrQuadEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillAA,
-                                     localMatrix, usesLocalCoords));
+            case GrClipEdgeType::kFillAA:       // fall through
             case GrClipEdgeType::kHairlineAA:
                 if (!caps.shaderCaps()->shaderDerivativeSupport()) {
                     return nullptr;
                 }
-                return sk_sp<GrGeometryProcessor>(
-                    new GrQuadEffect(color, viewMatrix, coverage,
-                                     GrClipEdgeType::kHairlineAA, localMatrix,
-                                     usesLocalCoords));
+                break;
             case GrClipEdgeType::kFillBW:
-                return sk_sp<GrGeometryProcessor>(
-                    new GrQuadEffect(color, viewMatrix, coverage, GrClipEdgeType::kFillBW,
-                                     localMatrix, usesLocalCoords));
-            default:
+                break;
+            default: // kInverseFillBW and kInverseFillAA
                 return nullptr;
         }
+
+        return arena->make<GrQuadEffect>(color, viewMatrix, coverage, edgeType,
+                                         localMatrix, usesLocalCoords);
     }
 
     ~GrQuadEffect() override;
@@ -193,6 +182,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrQuadEffect(const SkPMColor4f&, const SkMatrix& viewMatrix, uint8_t coverage, GrClipEdgeType,
                  const SkMatrix& localMatrix, bool usesLocalCoords);
 
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index 6d7b431..973ef45 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -145,32 +145,30 @@
 void GrGLBicubicEffect::onSetData(const GrGLSLProgramDataManager& pdman,
                                   const GrFragmentProcessor& processor) {
     const GrBicubicEffect& bicubicEffect = processor.cast<GrBicubicEffect>();
-    GrTextureProxy* proxy = processor.textureSampler(0).proxy();
-    GrTexture* texture = proxy->peekTexture();
+    GrSurfaceProxy* proxy = processor.textureSampler(0).proxy();
+    SkISize textureDims = proxy->backingStoreDimensions();
 
     float dims[4] = {0, 0, 0, 0};
     if (bicubicEffect.direction() != GrBicubicEffect::Direction::kY) {
-        dims[0] = 1.0f / texture->width();
-        dims[2] = texture->width();
+        dims[0] = 1.0f / textureDims.width();
+        dims[2] = textureDims.width();
     }
     if (bicubicEffect.direction() != GrBicubicEffect::Direction::kX) {
-        dims[1] = 1.0f / texture->height();
-        dims[3] = texture->height();
+        dims[1] = 1.0f / textureDims.height();
+        dims[3] = textureDims.height();
     }
     pdman.set4fv(fDimensions, 1, dims);
     fDomain.setData(pdman, bicubicEffect.domain(), proxy,
                     processor.textureSampler(0).samplerState());
 }
 
-GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy, GrColorType srcColorType,
-                                 const SkMatrix& matrix, const SkRect& domain,
-                                 const GrSamplerState::WrapMode wrapModes[2],
+GrBicubicEffect::GrBicubicEffect(sk_sp<GrSurfaceProxy> proxy, const SkMatrix& matrix,
+                                 const SkRect& domain, const GrSamplerState::WrapMode wrapModes[2],
                                  GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY,
                                  Direction direction, SkAlphaType alphaType)
         : INHERITED{kGrBicubicEffect_ClassID,
                     ModulateForSamplerOptFlags(
-                            srcColorType,
-                            GrTextureDomain::IsDecalSampled(wrapModes, modeX, modeY))}
+                            alphaType, GrTextureDomain::IsDecalSampled(wrapModes, modeX, modeY))}
         , fCoordTransform(matrix, proxy.get())
         , fDomain(proxy.get(), domain, modeX, modeY)
         , fTextureSampler(std::move(proxy),
@@ -227,8 +225,8 @@
             direction = Direction::kXY;
             break;
     }
-    return GrBicubicEffect::Make(d->textureProxy(texIdx), d->textureProxyColorType(texIdx),
-                                 SkMatrix::I(), kClampClamp, direction, alphaType);
+    return GrBicubicEffect::Make(d->textureProxy(texIdx), SkMatrix::I(), kClampClamp, direction,
+                                 alphaType);
 }
 #endif
 
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index 5e274e0..319f1d9 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -44,32 +44,28 @@
     /**
      * Create a Mitchell filter effect with specified texture matrix with clamp wrap mode.
      */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
                                                      const SkMatrix& matrix,
                                                      Direction direction,
                                                      SkAlphaType alphaType) {
         static constexpr GrSamplerState::WrapMode kClampClamp[] = {
                 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
-        return Make(std::move(proxy), srcColorType, matrix, kClampClamp,
-                    GrTextureDomain::kIgnore_Mode, GrTextureDomain::kIgnore_Mode, direction,
-                    alphaType);
+        return Make(std::move(proxy), matrix, kClampClamp, GrTextureDomain::kIgnore_Mode,
+                    GrTextureDomain::kIgnore_Mode, direction, alphaType);
     }
 
     /**
      * Create a Mitchell filter effect with specified texture matrix and x/y tile modes.
      */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
                                                      const SkMatrix& matrix,
                                                      const GrSamplerState::WrapMode wrapModes[2],
                                                      Direction direction,
                                                      SkAlphaType alphaType) {
         // Ignore the domain on x and y, since this factory relies solely on the wrap mode of the
         // sampler to constrain texture coordinates
-        return Make(std::move(proxy), srcColorType, matrix, wrapModes,
-                    GrTextureDomain::kIgnore_Mode, GrTextureDomain::kIgnore_Mode, direction,
-                    alphaType);
+        return Make(std::move(proxy), matrix, wrapModes, GrTextureDomain::kIgnore_Mode,
+                    GrTextureDomain::kIgnore_Mode, direction, alphaType);
     }
 
     /**
@@ -77,8 +73,7 @@
      * supports providing modes for the texture domain explicitly, in the event that it should
      * override the behavior of the sampler's tile mode (e.g. clamp to border unsupported).
      */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
                                                      const SkMatrix& matrix,
                                                      const GrSamplerState::WrapMode wrapModes[2],
                                                      GrTextureDomain::Mode modeX,
@@ -94,24 +89,22 @@
                     SkIRect::MakeSize(proxy->dimensions()), modeX, modeY);
         }
         return std::unique_ptr<GrFragmentProcessor>(
-                new GrBicubicEffect(std::move(proxy), srcColorType, matrix, resolvedDomain,
-                                    wrapModes, modeX, modeY, direction, alphaType));
+                new GrBicubicEffect(std::move(proxy), matrix, resolvedDomain, wrapModes, modeX,
+                                    modeY, direction, alphaType));
     }
 
     /**
      * Create a Mitchell filter effect with a texture matrix and a domain.
      */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
                                                      const SkMatrix& matrix,
                                                      const SkRect& domain,
                                                      Direction direction,
                                                      SkAlphaType alphaType) {
         static const GrSamplerState::WrapMode kClampClamp[] = {
                 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
-        return Make(std::move(proxy), srcColorType, matrix, kClampClamp,
-                    GrTextureDomain::kClamp_Mode, GrTextureDomain::kClamp_Mode, direction,
-                    alphaType, &domain);
+        return Make(std::move(proxy), matrix, kClampClamp, GrTextureDomain::kClamp_Mode,
+                    GrTextureDomain::kClamp_Mode, direction, alphaType, &domain);
     }
 
     /**
@@ -125,10 +118,9 @@
                                  GrSamplerState::Filter* filterMode);
 
 private:
-    GrBicubicEffect(sk_sp<GrTextureProxy>, GrColorType srcColorType, const SkMatrix& matrix,
-                    const SkRect& domain, const GrSamplerState::WrapMode wrapModes[2],
-                    GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY, Direction direction,
-                    SkAlphaType alphaType);
+    GrBicubicEffect(sk_sp<GrSurfaceProxy>, const SkMatrix& matrix, const SkRect& domain,
+                    const GrSamplerState::WrapMode wrapModes[2], GrTextureDomain::Mode modeX,
+                    GrTextureDomain::Mode modeY, Direction direction, SkAlphaType);
     explicit GrBicubicEffect(const GrBicubicEffect&);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.cpp b/src/gpu/effects/GrBitmapTextGeoProc.cpp
index 67adfc5..5c710cc 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.cpp
+++ b/src/gpu/effects/GrBitmapTextGeoProc.cpp
@@ -76,7 +76,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const GrBitmapTextGeoProc& btgp = gp.cast<GrBitmapTextGeoProc>();
         if (btgp.color() != fColor && !btgp.hasVertexColor()) {
             pdman.set4fv(fColorUniform, 1, btgp.color().vec());
@@ -92,7 +92,7 @@
                         1.0f / atlasDimensions.fHeight);
             fAtlasDimensions = atlasDimensions;
         }
-        this->setTransformDataHelper(btgp.localMatrix(), pdman, &transformIter);
+        this->setTransformDataHelper(btgp.localMatrix(), pdman, transformRange);
     }
 
     static inline void GenKey(const GrGeometryProcessor& proc,
@@ -164,6 +164,8 @@
                                         int numActiveProxies,
                                         const GrSamplerState& params) {
     SkASSERT(numActiveProxies <= kMaxTextures);
+    // Just to make sure we don't try to add too many proxies
+    numActiveProxies = SkTMin(numActiveProxies, kMaxTextures);
 
     if (!fTextureSamplers[0].isInitialized()) {
         fAtlasDimensions = proxies[0]->dimensions();
@@ -196,7 +198,7 @@
 
 #if GR_TEST_UTILS
 
-sk_sp<GrGeometryProcessor> GrBitmapTextGeoProc::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* GrBitmapTextGeoProc::TestCreate(GrProcessorTestData* d) {
     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
                                         : GrProcessorUnitTest::kAlphaTextureIdx;
     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
@@ -225,7 +227,7 @@
             break;
     }
 
-    return GrBitmapTextGeoProc::Make(*d->caps()->shaderCaps(),
+    return GrBitmapTextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
                                      SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                      d->fRandom->nextBool(),
                                      proxies, 1, samplerState, format,
diff --git a/src/gpu/effects/GrBitmapTextGeoProc.h b/src/gpu/effects/GrBitmapTextGeoProc.h
index 9a6096b..c6912d3 100644
--- a/src/gpu/effects/GrBitmapTextGeoProc.h
+++ b/src/gpu/effects/GrBitmapTextGeoProc.h
@@ -8,6 +8,7 @@
 #ifndef GrBitmapTextGeoProc_DEFINED
 #define GrBitmapTextGeoProc_DEFINED
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrProcessor.h"
 
@@ -23,15 +24,15 @@
 public:
     static constexpr int kMaxTextures = 4;
 
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps& caps,
-                                           const SkPMColor4f& color, bool wideColor,
-                                           const sk_sp<GrTextureProxy>* proxies,
-                                           int numActiveProxies,
-                                           const GrSamplerState& p, GrMaskFormat format,
-                                           const SkMatrix& localMatrix, bool usesW) {
-        return sk_sp<GrGeometryProcessor>(
-            new GrBitmapTextGeoProc(caps, color, wideColor, proxies, numActiveProxies, p, format,
-                                    localMatrix, usesW));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps& caps,
+                                     const SkPMColor4f& color, bool wideColor,
+                                     const sk_sp<GrTextureProxy>* proxies,
+                                     int numActiveProxies,
+                                     const GrSamplerState& p, GrMaskFormat format,
+                                     const SkMatrix& localMatrix, bool usesW) {
+        return arena->make<GrBitmapTextGeoProc>(caps, color, wideColor, proxies, numActiveProxies,
+                                                p, format, localMatrix, usesW);
     }
 
     ~GrBitmapTextGeoProc() override {}
@@ -55,6 +56,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrBitmapTextGeoProc(const GrShaderCaps&, const SkPMColor4f&, bool wideColor,
                         const sk_sp<GrTextureProxy>* proxies, int numProxies,
                         const GrSamplerState& params, GrMaskFormat format,
diff --git a/src/gpu/effects/GrConfigConversionEffect.fp b/src/gpu/effects/GrConfigConversionEffect.fp
index 0387c3b..7338b7d 100644
--- a/src/gpu/effects/GrConfigConversionEffect.fp
+++ b/src/gpu/effects/GrConfigConversionEffect.fp
@@ -64,7 +64,6 @@
         // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
         // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
         sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
-        GrColorType dataColorType = SkColorTypeToGrColorType(image->colorType());
         sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(std::move(image),
                                                                             1,
                                                                             SkBudgeted::kYes,
@@ -87,7 +86,7 @@
         std::unique_ptr<GrFragmentProcessor> upmToPM(
                 new GrConfigConversionEffect(PMConversion::kToPremul));
 
-        paint1.addColorTextureProcessor(dataProxy, dataColorType, SkMatrix::I());
+        paint1.addColorTextureProcessor(dataProxy, kPremul_SkAlphaType, SkMatrix::I());
         paint1.addColorFragmentProcessor(pmToUPM->clone());
         paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
@@ -101,14 +100,16 @@
         // draw
         tempRTC->discard();
 
-        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), kColorType, SkMatrix::I());
+        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), kUnpremul_SkAlphaType,
+                                        SkMatrix::I());
         paint2.addColorFragmentProcessor(std::move(upmToPM));
         paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
         tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect,
                                 kRect);
 
-        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), kColorType, SkMatrix::I());
+        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), kPremul_SkAlphaType,
+                                        SkMatrix::I());
         paint3.addColorFragmentProcessor(std::move(pmToUPM));
         paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.cpp b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
index 50b40aa..1122d0e 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.cpp
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.cpp
@@ -163,7 +163,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         const GrDistanceFieldA8TextGeoProc& dfa8gp = proc.cast<GrDistanceFieldA8TextGeoProc>();
 
 #ifdef SK_GAMMA_APPLY_TO_A8
@@ -183,7 +183,7 @@
                         1.0f / atlasDimensions.fHeight);
             fAtlasDimensions = atlasDimensions;
         }
-        this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, &transformIter);
+        this->setTransformDataHelper(dfa8gp.localMatrix(), pdman, transformRange);
     }
 
     static inline void GenKey(const GrGeometryProcessor& gp,
@@ -253,6 +253,8 @@
                                                  int numProxies,
                                                  const GrSamplerState& params) {
     SkASSERT(numProxies <= kMaxTextures);
+    // Just to make sure we don't try to add too many proxies
+    numProxies = SkTMin(numProxies, kMaxTextures);
 
     if (!fTextureSamplers[0].isInitialized()) {
         fAtlasDimensions = proxies[0]->dimensions();
@@ -284,7 +286,7 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldA8TextGeoProc);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* GrDistanceFieldA8TextGeoProc::TestCreate(GrProcessorTestData* d) {
     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
                                         : GrProcessorUnitTest::kAlphaTextureIdx;
     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
@@ -309,7 +311,7 @@
 #ifdef SK_GAMMA_APPLY_TO_A8
     float lum = d->fRandom->nextF();
 #endif
-    return GrDistanceFieldA8TextGeoProc::Make(*d->caps()->shaderCaps(),
+    return GrDistanceFieldA8TextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
                                               proxies, 1,
                                               samplerState,
 #ifdef SK_GAMMA_APPLY_TO_A8
@@ -461,8 +463,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
-                 FPCoordTransformIter&& transformIter) override {
-
+                 const CoordTransformRange& transformRange) override {
         const GrDistanceFieldPathGeoProc& dfpgp = proc.cast<GrDistanceFieldPathGeoProc>();
 
         if (dfpgp.matrix().hasPerspective() && !fMatrix.cheapEqualTo(dfpgp.matrix())) {
@@ -482,9 +483,9 @@
         }
 
         if (dfpgp.matrix().hasPerspective()) {
-            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+            this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
         } else {
-            this->setTransformDataHelper(dfpgp.matrix(), pdman, &transformIter);
+            this->setTransformDataHelper(dfpgp.matrix(), pdman, transformRange);
         }
     }
 
@@ -548,6 +549,8 @@
                                                int numProxies,
                                                const GrSamplerState& params) {
     SkASSERT(numProxies <= kMaxTextures);
+    // Just to make sure we don't try to add too many proxies
+    numProxies = SkTMin(numProxies, kMaxTextures);
 
     if (!fTextureSamplers[0].isInitialized()) {
         fAtlasDimensions = proxies[0]->dimensions();
@@ -580,7 +583,7 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldPathGeoProc);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* GrDistanceFieldPathGeoProc::TestCreate(GrProcessorTestData* d) {
     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
                                         : GrProcessorUnitTest::kAlphaTextureIdx;
     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
@@ -602,7 +605,7 @@
         flags |= d->fRandom->nextBool() ? kScaleOnly_DistanceFieldEffectFlag : 0;
     }
 
-    return GrDistanceFieldPathGeoProc::Make(*d->caps()->shaderCaps(),
+    return GrDistanceFieldPathGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(),
                                             GrTest::TestMatrix(d->fRandom),
                                             d->fRandom->nextBool(),
                                             proxies, 1,
@@ -787,7 +790,7 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& processor,
-                 FPCoordTransformIter&& transformIter) override {
+                 const CoordTransformRange& transformRange) override {
         SkASSERT(fDistanceAdjustUni.isValid());
 
         const GrDistanceFieldLCDTextGeoProc& dflcd = processor.cast<GrDistanceFieldLCDTextGeoProc>();
@@ -808,7 +811,7 @@
                         1.0f / atlasDimensions.fHeight);
             fAtlasDimensions = atlasDimensions;
         }
-        this->setTransformDataHelper(dflcd.localMatrix(), pdman, &transformIter);
+        this->setTransformDataHelper(dflcd.localMatrix(), pdman, transformRange);
     }
 
     static inline void GenKey(const GrGeometryProcessor& gp,
@@ -874,6 +877,8 @@
                                                   int numProxies,
                                                   const GrSamplerState& params) {
     SkASSERT(numProxies <= kMaxTextures);
+    // Just to make sure we don't try to add too many proxies
+    numProxies = SkTMin(numProxies, kMaxTextures);
 
     if (!fTextureSamplers[0].isInitialized()) {
         fAtlasDimensions = proxies[0]->dimensions();
@@ -905,7 +910,7 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrDistanceFieldLCDTextGeoProc);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* GrDistanceFieldLCDTextGeoProc::TestCreate(GrProcessorTestData* d) {
     int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx :
                                           GrProcessorUnitTest::kAlphaTextureIdx;
     sk_sp<GrTextureProxy> proxies[kMaxTextures] = {
@@ -928,7 +933,7 @@
     }
     flags |= d->fRandom->nextBool() ? kBGR_DistanceFieldEffectFlag : 0;
     SkMatrix localMatrix = GrTest::TestMatrix(d->fRandom);
-    return GrDistanceFieldLCDTextGeoProc::Make(*d->caps()->shaderCaps(), proxies, 1, samplerState,
-                                               wa, flags, localMatrix);
+    return GrDistanceFieldLCDTextGeoProc::Make(d->allocator(), *d->caps()->shaderCaps(), proxies,
+                                               1, samplerState, wa, flags, localMatrix);
 }
 #endif
diff --git a/src/gpu/effects/GrDistanceFieldGeoProc.h b/src/gpu/effects/GrDistanceFieldGeoProc.h
index f55ca40..95e3a11 100644
--- a/src/gpu/effects/GrDistanceFieldGeoProc.h
+++ b/src/gpu/effects/GrDistanceFieldGeoProc.h
@@ -8,6 +8,7 @@
 #ifndef GrDistanceFieldGeoProc_DEFINED
 #define GrDistanceFieldGeoProc_DEFINED
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrProcessor.h"
 
@@ -57,22 +58,24 @@
 
     /** The local matrix should be identity if local coords are not required by the GrPipeline. */
 #ifdef SK_GAMMA_APPLY_TO_A8
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps& caps,
-                                           const sk_sp<GrTextureProxy>* proxies,
-                                           int numActiveProxies,
-                                           const GrSamplerState& params, float lum, uint32_t flags,
-                                           const SkMatrix& localMatrixIfUsesLocalCoords) {
-        return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
-                caps, proxies, numActiveProxies, params, lum, flags, localMatrixIfUsesLocalCoords));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps& caps,
+                                     const sk_sp<GrTextureProxy>* proxies,
+                                     int numActiveProxies,
+                                     const GrSamplerState& params, float lum, uint32_t flags,
+                                     const SkMatrix& localMatrixIfUsesLocalCoords) {
+        return arena->make<GrDistanceFieldA8TextGeoProc>(
+                caps, proxies, numActiveProxies, params, lum, flags, localMatrixIfUsesLocalCoords);
     }
 #else
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps& caps,
-                                           const sk_sp<GrTextureProxy>* proxies,
-                                           int numActiveProxies,
-                                           const GrSamplerState& params, uint32_t flags,
-                                           const SkMatrix& localMatrixIfUsesLocalCoords) {
-        return sk_sp<GrGeometryProcessor>(new GrDistanceFieldA8TextGeoProc(
-                caps, proxies, numActiveProxies, params, flags, localMatrixIfUsesLocalCoords));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps& caps,
+                                     const sk_sp<GrTextureProxy>* proxies,
+                                     int numActiveProxies,
+                                     const GrSamplerState& params, uint32_t flags,
+                                     const SkMatrix& localMatrixIfUsesLocalCoords) {
+        return arena->make<GrDistanceFieldA8TextGeoProc>(
+                caps, proxies, numActiveProxies, params, flags, localMatrixIfUsesLocalCoords);
     }
 #endif
 
@@ -97,6 +100,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrDistanceFieldA8TextGeoProc(const GrShaderCaps& caps,
                                  const sk_sp<GrTextureProxy>* proxies,
                                  int numActiveProxies,
@@ -135,15 +140,15 @@
     static constexpr int kMaxTextures = 4;
 
     /** The local matrix should be identity if local coords are not required by the GrPipeline. */
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps& caps,
-                                           const SkMatrix& matrix,
-                                           bool wideColor,
-                                           const sk_sp<GrTextureProxy>* proxies,
-                                           int numActiveProxies,
-                                           const GrSamplerState& params, uint32_t flags) {
-        return sk_sp<GrGeometryProcessor>(
-            new GrDistanceFieldPathGeoProc(caps, matrix, wideColor, proxies, numActiveProxies,
-                                           params, flags));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps& caps,
+                                     const SkMatrix& matrix,
+                                     bool wideColor,
+                                     const sk_sp<GrTextureProxy>* proxies,
+                                     int numActiveProxies,
+                                     const GrSamplerState& params, uint32_t flags) {
+        return arena->make<GrDistanceFieldPathGeoProc>(caps, matrix, wideColor, proxies,
+                                                       numActiveProxies, params, flags);
     }
 
     ~GrDistanceFieldPathGeoProc() override {}
@@ -164,6 +169,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrDistanceFieldPathGeoProc(const GrShaderCaps& caps,
                                const SkMatrix& matrix,
                                bool wideColor,
@@ -211,16 +218,17 @@
         }
     };
 
-    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps& caps,
-                                           const sk_sp<GrTextureProxy>* proxies,
-                                           int numActiveProxies,
-                                           const GrSamplerState& params,
-                                           DistanceAdjust distanceAdjust,
-                                           uint32_t flags,
-                                           const SkMatrix& localMatrixIfUsesLocalCoords) {
-        return sk_sp<GrGeometryProcessor>(
-            new GrDistanceFieldLCDTextGeoProc(caps, proxies, numActiveProxies, params,
-                                              distanceAdjust, flags, localMatrixIfUsesLocalCoords));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrShaderCaps& caps,
+                                     const sk_sp<GrTextureProxy>* proxies,
+                                     int numActiveProxies,
+                                     const GrSamplerState& params,
+                                     DistanceAdjust distanceAdjust,
+                                     uint32_t flags,
+                                     const SkMatrix& localMatrixIfUsesLocalCoords) {
+        return arena->make<GrDistanceFieldLCDTextGeoProc>(caps, proxies, numActiveProxies, params,
+                                                          distanceAdjust, flags,
+                                                          localMatrixIfUsesLocalCoords);
     }
 
     ~GrDistanceFieldLCDTextGeoProc() override {}
@@ -242,6 +250,8 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrDistanceFieldLCDTextGeoProc(const GrShaderCaps& caps, const sk_sp<GrTextureProxy>* proxies,
                                   int numActiveProxies, const GrSamplerState& params,
                                   DistanceAdjust wa, uint32_t flags, const SkMatrix& localMatrix);
diff --git a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
index fd70616..231c1b6 100644
--- a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
+++ b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp
@@ -211,16 +211,15 @@
 }
 
 GrGaussianConvolutionFragmentProcessor::GrGaussianConvolutionFragmentProcessor(
-                                                            sk_sp<GrTextureProxy> proxy,
-                                                            GrColorType srcColorType,
-                                                            Direction direction,
-                                                            int radius,
-                                                            float gaussianSigma,
-                                                            GrTextureDomain::Mode mode,
-                                                            int bounds[2])
+        sk_sp<GrSurfaceProxy> proxy,
+        SkAlphaType alphaType,
+        Direction direction,
+        int radius,
+        float gaussianSigma,
+        GrTextureDomain::Mode mode,
+        int bounds[2])
         : INHERITED(kGrGaussianConvolutionFragmentProcessor_ClassID,
-                    ModulateForSamplerOptFlags(srcColorType,
-                                               mode == GrTextureDomain::kDecal_Mode))
+                    ModulateForSamplerOptFlags(alphaType, mode == GrTextureDomain::kDecal_Mode))
         , fCoordTransform(proxy.get())
         , fTextureSampler(std::move(proxy))
         , fRadius(radius)
@@ -298,8 +297,10 @@
     int radius = d->fRandom->nextRangeU(1, kMaxKernelRadius);
     float sigma = radius / 3.f;
 
+    auto alphaType = static_cast<SkAlphaType>(
+            d->fRandom->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
     return GrGaussianConvolutionFragmentProcessor::Make(
-            std::move(proxy), d->textureProxyColorType(texIdx),
-            dir, radius, sigma, static_cast<GrTextureDomain::Mode>(modeIdx), bounds);
+            std::move(proxy), alphaType, dir, radius, sigma,
+            static_cast<GrTextureDomain::Mode>(modeIdx), bounds);
 }
 #endif
diff --git a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h
index 843ed30..205b981 100644
--- a/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h
+++ b/src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h
@@ -22,15 +22,15 @@
     enum class Direction { kX, kY };
 
     /// Convolve with a Gaussian kernel
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      Direction dir,
                                                      int halfWidth,
                                                      float gaussianSigma,
                                                      GrTextureDomain::Mode mode,
                                                      int* bounds) {
         return std::unique_ptr<GrFragmentProcessor>(new GrGaussianConvolutionFragmentProcessor(
-                std::move(proxy), srcColorType, dir, halfWidth, gaussianSigma, mode, bounds));
+                std::move(proxy), alphaType, dir, halfWidth, gaussianSigma, mode, bounds));
     }
 
     const float* kernel() const { return fKernel; }
@@ -72,8 +72,8 @@
 
 private:
     /// Convolve with a Gaussian kernel
-    GrGaussianConvolutionFragmentProcessor(sk_sp<GrTextureProxy>, GrColorType srcColorType,
-                                           Direction, int halfWidth, float gaussianSigma,
+    GrGaussianConvolutionFragmentProcessor(sk_sp<GrSurfaceProxy>, SkAlphaType alphaType, Direction,
+                                           int halfWidth, float gaussianSigma,
                                            GrTextureDomain::Mode mode, int bounds[2]);
 
     explicit GrGaussianConvolutionFragmentProcessor(const GrGaussianConvolutionFragmentProcessor&);
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index 86df7a0..1f70a53 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -125,13 +125,13 @@
 void GrGLMatrixConvolutionEffect::onSetData(const GrGLSLProgramDataManager& pdman,
                                             const GrFragmentProcessor& processor) {
     const GrMatrixConvolutionEffect& conv = processor.cast<GrMatrixConvolutionEffect>();
-    GrTextureProxy* proxy = conv.textureSampler(0).proxy();
-    GrTexture* texture = proxy->peekTexture();
+    GrSurfaceProxy* proxy = conv.textureSampler(0).proxy();
+    SkISize textureDims = proxy->backingStoreDimensions();
 
     float imageIncrement[2];
     float ySign = proxy->origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
-    imageIncrement[0] = 1.0f / texture->width();
-    imageIncrement[1] = ySign / texture->height();
+    imageIncrement[0] = 1.0f / textureDims.width();
+    imageIncrement[1] = ySign / textureDims.height();
     pdman.set2fv(fImageIncrementUni, 1, imageIncrement);
     pdman.set2fv(fKernelOffsetUni, 1, conv.kernelOffset());
     int kernelCount = conv.kernelSize().width() * conv.kernelSize().height();
@@ -143,7 +143,7 @@
     fDomain.setData(pdman, conv.domain(), proxy, conv.textureSampler(0).samplerState());
 }
 
-GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(sk_sp<GrTextureProxy> srcProxy,
+GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(sk_sp<GrSurfaceProxy> srcProxy,
                                                      const SkIRect& srcBounds,
                                                      const SkISize& kernelSize,
                                                      const SkScalar* kernel,
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.h b/src/gpu/effects/GrMatrixConvolutionEffect.h
index 212d5c0..ac36de2 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.h
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.h
@@ -16,7 +16,7 @@
 
 class GrMatrixConvolutionEffect : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> srcProxy,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> srcProxy,
                                                      const SkIRect& srcBounds,
                                                      const SkISize& kernelSize,
                                                      const SkScalar* kernel,
@@ -57,7 +57,7 @@
 private:
     // srcProxy is the texture that is going to be convolved
     // srcBounds is the subset of 'srcProxy' that will be used (e.g., for clamp mode)
-    GrMatrixConvolutionEffect(sk_sp<GrTextureProxy> srcProxy,
+    GrMatrixConvolutionEffect(sk_sp<GrSurfaceProxy> srcProxy,
                               const SkIRect& srcBounds,
                               const SkISize& kernelSize,
                               const SkScalar* kernel,
diff --git a/src/gpu/effects/GrShadowGeoProc.cpp b/src/gpu/effects/GrShadowGeoProc.cpp
index 5af096c..a1c4c54 100644
--- a/src/gpu/effects/GrShadowGeoProc.cpp
+++ b/src/gpu/effects/GrShadowGeoProc.cpp
@@ -43,17 +43,16 @@
                              args.fFPCoordTransformHandler);
 
         fragBuilder->codeAppend("half d = length(shadowParams.xy);");
-        fragBuilder->codeAppend("half distance = shadowParams.z * (1.0 - d);");
-
-        fragBuilder->codeAppend("half factor = 1.0 - clamp(distance, 0.0, 1.0);");
-        fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;");
-        fragBuilder->codeAppendf("%s = half4(factor);",
-                                 args.fOutputCoverage);
+        fragBuilder->codeAppend("float2 uv = float2(shadowParams.z * (1.0 - d), 0.5);");
+        fragBuilder->codeAppend("half factor = ");
+        fragBuilder->appendTextureLookup(args.fTexSamplers[0], "uv", kFloat2_GrSLType);
+        fragBuilder->codeAppend(".a;");
+        fragBuilder->codeAppendf("%s = half4(factor);", args.fOutputCoverage);
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
-                 FPCoordTransformIter&& transformIter) override {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                 const CoordTransformRange& transformRange) override {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
     }
 
 private:
@@ -62,11 +61,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrRRectShadowGeoProc::GrRRectShadowGeoProc() : INHERITED(kGrRRectShadowGeoProc_ClassID) {
+GrRRectShadowGeoProc::GrRRectShadowGeoProc(const GrTextureProxy* lut)
+        : INHERITED(kGrRRectShadowGeoProc_ClassID) {
     fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
     fInColor = {"inColor", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
     fInShadowParams = {"inShadowParams", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
     this->setVertexAttributes(&fInPosition, 3);
+
+    SkASSERT(lut);
+    fLUTTextureSampler.reset(GrSamplerState::ClampBilerp(), lut->backendFormat(),
+                             lut->textureSwizzle());
+    this->setTextureSamplerCnt(1);
 }
 
 GrGLSLPrimitiveProcessor* GrRRectShadowGeoProc::createGLSLInstance(const GrShaderCaps&) const {
@@ -78,7 +83,8 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrRRectShadowGeoProc);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> GrRRectShadowGeoProc::TestCreate(GrProcessorTestData* d) {
-    return GrRRectShadowGeoProc::Make();
+GrGeometryProcessor* GrRRectShadowGeoProc::TestCreate(GrProcessorTestData* d) {
+    return GrRRectShadowGeoProc::Make(d->allocator(),
+                                      d->textureProxy(GrProcessorUnitTest::kAlphaTextureIdx).get());
 }
 #endif
diff --git a/src/gpu/effects/GrShadowGeoProc.h b/src/gpu/effects/GrShadowGeoProc.h
index b821a6a..1b9b49b 100644
--- a/src/gpu/effects/GrShadowGeoProc.h
+++ b/src/gpu/effects/GrShadowGeoProc.h
@@ -8,6 +8,7 @@
 #ifndef GrShadowGeoProc_DEFINED
 #define GrShadowGeoProc_DEFINED
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrProcessor.h"
 
@@ -19,8 +20,8 @@
  */
 class GrRRectShadowGeoProc : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make() {
-        return sk_sp<GrGeometryProcessor>(new GrRRectShadowGeoProc());
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, const GrTextureProxy* lut) {
+        return arena->make<GrRRectShadowGeoProc>(lut);
     }
 
     const char* name() const override { return "RRectShadow"; }
@@ -35,9 +36,14 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
-    GrRRectShadowGeoProc();
+    friend class ::SkArenaAlloc; // for access to ctor
+
+    GrRRectShadowGeoProc(const GrTextureProxy* lut);
+
+    const TextureSampler& onTextureSampler(int i) const override { return fLUTTextureSampler; }
 
     GrColor          fColor;
+    TextureSampler   fLUTTextureSampler;
 
     Attribute fInPosition;
     Attribute fInColor;
diff --git a/src/gpu/effects/GrSimpleTextureEffect.fp b/src/gpu/effects/GrSimpleTextureEffect.fp
index 5c30cc0..cf4dd31 100644
--- a/src/gpu/effects/GrSimpleTextureEffect.fp
+++ b/src/gpu/effects/GrSimpleTextureEffect.fp
@@ -9,7 +9,7 @@
 in half4x4 matrix;
 
 @constructorParams {
-    GrColorType srcColorType,
+    SkAlphaType alphaType,
     GrSamplerState samplerParams
 }
 
@@ -22,35 +22,35 @@
 }
 
 @make {
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix) {
         return std::unique_ptr<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(std::move(proxy), matrix, srcColorType,
+            new GrSimpleTextureEffect(std::move(proxy), matrix, alphaType,
                     GrSamplerState(GrSamplerState::WrapMode::kClamp, GrSamplerState::Filter::kNearest)));
     }
 
     /* clamp mode */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix,
                                                      GrSamplerState::Filter filter) {
         return std::unique_ptr<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(std::move(proxy), matrix, srcColorType,
+            new GrSimpleTextureEffect(std::move(proxy), matrix, alphaType,
                                       GrSamplerState(GrSamplerState::WrapMode::kClamp, filter)));
      }
 
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix,
                                                      const GrSamplerState& p) {
         return std::unique_ptr<GrFragmentProcessor>(
-            new GrSimpleTextureEffect(std::move(proxy), matrix, srcColorType, p));
+            new GrSimpleTextureEffect(std::move(proxy), matrix, alphaType, p));
     }
 }
 
 @optimizationFlags {
-    ModulateForSamplerOptFlags(srcColorType,
+    ModulateForSamplerOptFlags(alphaType,
             samplerParams.wrapModeX() == GrSamplerState::WrapMode::kClampToBorder ||
             samplerParams.wrapModeY() == GrSamplerState::WrapMode::kClampToBorder)
 }
@@ -76,6 +76,7 @@
                                                                : GrSamplerState::Filter::kNearest);
 
     const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
+    auto alphaType = static_cast<SkAlphaType>(
+            testData->fRandom->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
     return GrSimpleTextureEffect::Make(testData->textureProxy(texIdx),
-                                       testData->textureProxyColorType(texIdx), matrix, params);
-}
+                                       alphaType, matrix, params);}
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index 9269ebf..df65774 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -76,6 +76,12 @@
             bool v = *(((bool*) inputs) + offset);
             inputMap.insert(std::make_pair(name, SkSL::Program::Settings::Value(v)));
             offset += sizeof(bool);
+        } else if (&v->fType == fCompiler.context().fFloat2_Type.get() ||
+                   &v->fType == fCompiler.context().fHalf2_Type.get()) {
+            offset = SkAlign4(offset) + sizeof(float) * 2;
+        } else if (&v->fType == fCompiler.context().fFloat3_Type.get() ||
+                   &v->fType == fCompiler.context().fHalf3_Type.get()) {
+            offset = SkAlign4(offset) + sizeof(float) * 3;
         } else if (&v->fType == fCompiler.context().fFloat4_Type.get() ||
                    &v->fType == fCompiler.context().fHalf4_Type.get()) {
             offset = SkAlign4(offset) + sizeof(float) * 4;
@@ -103,6 +109,10 @@
    if (result == SkSL::Layout::CType::kDefault) {
         if (&v.fType == context.fFloat_Type.get()) {
             result = SkSL::Layout::CType::kFloat;
+        } else if (&v.fType == context.fFloat2_Type.get()) {
+            result = SkSL::Layout::CType::kFloat2;
+        } else if (&v.fType == context.fFloat3_Type.get()) {
+            result = SkSL::Layout::CType::kFloat3;
         } else if (&v.fType == context.fFloat4_Type.get()) {
            result = SkSL::Layout::CType::kSkRect;
         } else if (&v.fType == context.fHalf4_Type.get()) {
@@ -139,10 +149,22 @@
             return kFloat2_GrSLType;
         } else if (type == *fContext.fHalf2_Type) {
             return kHalf2_GrSLType;
+        } else if (type == *fContext.fFloat3_Type) {
+            return kFloat3_GrSLType;
+        } else if (type == *fContext.fHalf3_Type) {
+            return kHalf3_GrSLType;
         } else if (type == *fContext.fFloat4_Type) {
             return kFloat4_GrSLType;
         } else if (type == *fContext.fHalf4_Type) {
             return kHalf4_GrSLType;
+        } else if (type == *fContext.fFloat2x2_Type) {
+            return kFloat2x2_GrSLType;
+        } else if (type == *fContext.fHalf2x2_Type) {
+            return kHalf2x2_GrSLType;
+        } else if (type == *fContext.fFloat3x3_Type) {
+            return kFloat3x3_GrSLType;
+        } else if (type == *fContext.fHalf3x3_Type) {
+            return kHalf3x3_GrSLType;
         } else if (type == *fContext.fFloat4x4_Type) {
             return kFloat4x4_GrSLType;
         } else if (type == *fContext.fHalf4x4_Type) {
@@ -156,6 +178,64 @@
         SK_ABORT("unsupported uniform type");
     }
 
+    SkSL::String expandFormatArgs(const SkSL::String& raw,
+                                  const EmitArgs& args,
+                                  const std::vector<SkSL::Compiler::FormatArg> formatArgs,
+                                  const char* coordsName,
+                                  const std::vector<SkString>& childNames) {
+        SkSL::String result;
+        int substringStartIndex = 0;
+        int formatArgIndex = 0;
+        for (size_t i = 0; i < raw.length(); ++i) {
+            char c = raw[i];
+            if (c == '%') {
+                result += SkSL::StringFragment(raw.c_str() + substringStartIndex,
+                                               i - substringStartIndex);
+                ++i;
+                c = raw[i];
+                switch (c) {
+                    case 's': {
+                        const SkSL::Compiler::FormatArg& arg = formatArgs[formatArgIndex++];
+                        switch (arg.fKind) {
+                            case SkSL::Compiler::FormatArg::Kind::kInput:
+                                result += args.fInputColor;
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kOutput:
+                                result += args.fOutputColor;
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kCoordX:
+                                result += coordsName;
+                                result += ".x";
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kCoordY:
+                                result += coordsName;
+                                result += ".y";
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kUniform:
+                                result += args.fUniformHandler->getUniformCStr(
+                                                                       fUniformHandles[arg.fIndex]);
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kChildProcessor:
+                                result += childNames[arg.fIndex].c_str();
+                                break;
+                            case SkSL::Compiler::FormatArg::Kind::kFunctionName:
+                                SkASSERT((int) fFunctionNames.size() > arg.fIndex);
+                                result += fFunctionNames[arg.fIndex].c_str();
+                                break;
+                        }
+                        break;
+                    }
+                    default:
+                        result += c;
+                }
+                substringStartIndex = i + 1;
+            }
+        }
+        result += SkSL::StringFragment(raw.c_str() + substringStartIndex,
+                                       raw.length() - substringStartIndex);
+        return result;
+    }
+
     void emitCode(EmitArgs& args) override {
         for (const auto& v : fInAndUniformVars) {
             if (v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag && v->fType !=
@@ -167,69 +247,27 @@
             }
         }
         GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-        for (const auto& f : fFunctions) {
-            fFunctionNames.emplace_back();
-            fragBuilder->emitFunction(f.fReturnType,
-                                      f.fName.c_str(),
-                                      f.fParameters.size(),
-                                      f.fParameters.data(),
-                                      f.fBody.c_str(),
-                                      &fFunctionNames.back());
-        }
+        SkString coords = args.fTransformedCoords.count()
+            ? fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint)
+            : SkString("sk_FragCoord");
         std::vector<SkString> childNames;
         for (int i = 0; i < this->numChildProcessors(); ++i) {
             childNames.push_back(SkStringPrintf("_child%d", i));
             this->invokeChild(i, &childNames[i], args);
         }
-        int substringStartIndex = 0;
-        int formatArgIndex = 0;
-        SkString coords = args.fTransformedCoords.count()
-            ? fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint)
-            : SkString("sk_FragCoord");
-        for (size_t i = 0; i < fGLSL.length(); ++i) {
-            char c = fGLSL[i];
-            if (c == '%') {
-                fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex,
-                                        i - substringStartIndex);
-                ++i;
-                c = fGLSL[i];
-                switch (c) {
-                    case 's': {
-                        SkSL::Compiler::FormatArg& arg = fFormatArgs[formatArgIndex++];
-                        switch (arg.fKind) {
-                            case SkSL::Compiler::FormatArg::Kind::kInput:
-                                fragBuilder->codeAppend(args.fInputColor);
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kOutput:
-                                fragBuilder->codeAppend(args.fOutputColor);
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kCoordX:
-                                fragBuilder->codeAppendf("%s.x", coords.c_str());
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kCoordY:
-                                fragBuilder->codeAppendf("%s.y", coords.c_str());
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kUniform:
-                                fragBuilder->codeAppend(args.fUniformHandler->getUniformCStr(
-                                                                      fUniformHandles[arg.fIndex]));
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kChildProcessor:
-                                fragBuilder->codeAppend(childNames[arg.fIndex].c_str());
-                                break;
-                            case SkSL::Compiler::FormatArg::Kind::kFunctionName:
-                                fragBuilder->codeAppend(fFunctionNames[arg.fIndex].c_str());
-                                break;
-                        }
-                        break;
-                    }
-                    default:
-                        fragBuilder->codeAppendf("%c", c);
-                }
-                substringStartIndex = i + 1;
-            }
+        for (const auto& f : fFunctions) {
+            fFunctionNames.emplace_back();
+            SkSL::String body = this->expandFormatArgs(f.fBody.c_str(), args, f.fFormatArgs,
+                                                       coords.c_str(), childNames);
+            fragBuilder->emitFunction(f.fReturnType,
+                                      f.fName.c_str(),
+                                      f.fParameters.size(),
+                                      f.fParameters.data(),
+                                      body.c_str(),
+                                      &fFunctionNames.back());
         }
-        fragBuilder->codeAppend(fGLSL.c_str() + substringStartIndex,
-                                fGLSL.length() - substringStartIndex);
+        fragBuilder->codeAppend(this->expandFormatArgs(fGLSL.c_str(), args, fFormatArgs,
+                                                       coords.c_str(), childNames).c_str());
     }
 
     void onSetData(const GrGLSLProgramDataManager& pdman,
@@ -250,6 +288,30 @@
                     }
                     break;
                 }
+                case SkSL::Layout::CType::kFloat2: {
+                    offset = SkAlign4(offset);
+                    float f1 = *(float*) (inputs + offset);
+                    offset += sizeof(float);
+                    float f2 = *(float*) (inputs + offset);
+                    offset += sizeof(float);
+                    if (v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag) {
+                        pdman.set2f(fUniformHandles[uniformIndex++], f1, f2);
+                    }
+                    break;
+                }
+                case SkSL::Layout::CType::kFloat3: {
+                    offset = SkAlign4(offset);
+                    float f1 = *(float*) (inputs + offset);
+                    offset += sizeof(float);
+                    float f2 = *(float*) (inputs + offset);
+                    offset += sizeof(float);
+                    float f3 = *(float*) (inputs + offset);
+                    offset += sizeof(float);
+                    if (v->fModifiers.fFlags & SkSL::Modifiers::kUniform_Flag) {
+                        pdman.set3f(fUniformHandles[uniformIndex++], f1, f2, f3);
+                    }
+                    break;
+                }
                 case SkSL::Layout::CType::kSkPMColor4f:
                 case SkSL::Layout::CType::kSkRect: {
                     offset = SkAlign4(offset);
@@ -398,6 +460,19 @@
                             formatArgs, functions);
 }
 
+static void copy_floats_key(char* inputs, GrProcessorKeyBuilder* b, bool isIn, int count,
+                            size_t* offset, SkSL::String* key) {
+    if (isIn) {
+        for (size_t i = 0; i < sizeof(float) * count; ++i) {
+            (*key) += inputs[*offset + i];
+            b->add32(*(int32_t*) (inputs + *offset));
+            (*offset) += sizeof(float);
+        }
+    } else {
+        (*offset) += sizeof(float) * count;
+    }
+}
+
 void GrSkSLFP::onGetGLSLProcessorKey(const GrShaderCaps& caps,
                                      GrProcessorKeyBuilder* b) const {
     this->createFactory();
@@ -441,24 +516,19 @@
                 offset += sizeof(float);
                 break;
             }
+            case SkSL::Layout::CType::kFloat2:
+                copy_floats_key(inputs, b, v->fModifiers.fFlags & SkSL::Modifiers::kIn_Flag, 2,
+                                &offset, &fKey);
+                break;
+            case SkSL::Layout::CType::kFloat3:
+                copy_floats_key(inputs, b, v->fModifiers.fFlags & SkSL::Modifiers::kIn_Flag, 3,
+                                &offset, &fKey);
+                break;
             case SkSL::Layout::CType::kSkPMColor:
             case SkSL::Layout::CType::kSkPMColor4f:
             case SkSL::Layout::CType::kSkRect:
-                if (v->fModifiers.fFlags & SkSL::Modifiers::kIn_Flag) {
-                    for (size_t i = 0; i < sizeof(float) * 4; ++i) {
-                        fKey += inputs[offset + i];
-                    }
-                    b->add32(*(int32_t*) (inputs + offset));
-                    offset += sizeof(float);
-                    b->add32(*(int32_t*) (inputs + offset));
-                    offset += sizeof(float);
-                    b->add32(*(int32_t*) (inputs + offset));
-                    offset += sizeof(float);
-                    b->add32(*(int32_t*) (inputs + offset));
-                    offset += sizeof(float);
-                } else {
-                    offset += sizeof(float) * 4;
-                }
+                copy_floats_key(inputs, b, v->fModifiers.fFlags & SkSL::Modifiers::kIn_Flag, 4,
+                                &offset, &fKey);
                 break;
             default:
                 // unsupported input var type
diff --git a/src/gpu/effects/GrTextureDomain.cpp b/src/gpu/effects/GrTextureDomain.cpp
index 8bb662b..8a0014a 100644
--- a/src/gpu/effects/GrTextureDomain.cpp
+++ b/src/gpu/effects/GrTextureDomain.cpp
@@ -21,7 +21,7 @@
 
 #include <utility>
 
-GrTextureDomain::GrTextureDomain(GrTextureProxy* proxy, const SkRect& domain, Mode modeX,
+GrTextureDomain::GrTextureDomain(GrSurfaceProxy* proxy, const SkRect& domain, Mode modeX,
                                  Mode modeY, int index)
     : fModeX(modeX)
     , fModeY(modeY)
@@ -37,8 +37,7 @@
     // We don't currently handle domains that are empty or don't intersect the texture.
     // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
     // handle rects that do not intersect the [0..1]x[0..1] rect.
-    SkASSERT(domain.fLeft <= domain.fRight);
-    SkASSERT(domain.fTop <= domain.fBottom);
+    SkASSERT(domain.isSorted());
     fDomain.fLeft = SkScalarPin(domain.fLeft, 0.0f, kFullRect.fRight);
     fDomain.fRight = SkScalarPin(domain.fRight, fDomain.fLeft, kFullRect.fRight);
     fDomain.fTop = SkScalarPin(domain.fTop, 0.0f, kFullRect.fBottom);
@@ -47,6 +46,13 @@
     SkASSERT(fDomain.fTop <= fDomain.fBottom);
 }
 
+GrTextureDomain::GrTextureDomain(const SkRect& domain, Mode modeX, Mode modeY, int index)
+        : fDomain(domain), fModeX(modeX), fModeY(modeY), fIndex(index) {
+    // We don't currently handle domains that are empty or don't intersect the texture.
+    // It is OK if the domain rect is a line or point, but it should not be inverted.
+    SkASSERT(domain.isSorted());
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
 static SkString clamp_expression(GrTextureDomain::Mode mode, const char* inCoord,
@@ -76,6 +82,22 @@
     return clampedExpr;
 }
 
+void GrTextureDomain::GLDomain::sampleProcessor(const GrTextureDomain& textureDomain,
+                                                const char* inColor,
+                                                const char* outColor,
+                                                const SkString& inCoords,
+                                                GrGLSLFragmentProcessor* parent,
+                                                GrGLSLFragmentProcessor::EmitArgs& args,
+                                                int childIndex) {
+    auto appendProcessorSample = [parent, &args, childIndex, inColor](const char* coord) {
+        SkString outColor("childColor");
+        parent->invokeChild(childIndex, inColor, &outColor, args, coord);
+        return outColor;
+    };
+    this->sample(args.fFragBuilder, args.fUniformHandler, textureDomain, outColor, inCoords,
+                 appendProcessorSample);
+}
+
 void GrTextureDomain::GLDomain::sampleTexture(GrGLSLShaderBuilder* builder,
                                               GrGLSLUniformHandler* uniformHandler,
                                               const GrShaderCaps* shaderCaps,
@@ -84,6 +106,21 @@
                                               const SkString& inCoords,
                                               GrGLSLFragmentProcessor::SamplerHandle sampler,
                                               const char* inModulateColor) {
+    auto appendTextureSample = [&sampler, inModulateColor, builder](const char* coord) {
+        builder->codeAppend("half4 textureColor = ");
+        builder->appendTextureLookupAndModulate(inModulateColor, sampler, coord);
+        builder->codeAppend(";");
+        return SkString("textureColor");
+    };
+    this->sample(builder, uniformHandler, textureDomain, outColor, inCoords, appendTextureSample);
+}
+
+void GrTextureDomain::GLDomain::sample(GrGLSLShaderBuilder* builder,
+                                       GrGLSLUniformHandler* uniformHandler,
+                                       const GrTextureDomain& textureDomain,
+                                       const char* outColor,
+                                       const SkString& inCoords,
+                                       const std::function<AppendSample>& appendSample) {
     SkASSERT(!fHasMode || (textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY));
     SkDEBUGCODE(fModeX = textureDomain.modeX();)
     SkDEBUGCODE(fModeY = textureDomain.modeY();)
@@ -139,11 +176,8 @@
     }
     builder->codeAppend(";");
 
-    // Look up the texture sample at the clamped coordinate location
-    builder->codeAppend("half4 inside = ");
-    builder->appendTextureLookupAndModulate(inModulateColor, sampler, "clampedCoord",
-                                            kFloat2_GrSLType);
-    builder->codeAppend(";");
+    // Sample 'appendSample' at the clamped coordinate location.
+    SkString color = appendSample("clampedCoord");
 
     // Apply decal mode's transparency interpolation if needed
     if (decalX || decalY) {
@@ -170,175 +204,232 @@
         // is set to 1 and it becomes a simple linear blend between texture and transparent.
         builder->codeAppendf("if (err > %s.z) { err = 1.0; } else if (%s.z < 1) { err = 0.0; }",
                              fDecalName.c_str(), fDecalName.c_str());
-        builder->codeAppendf("%s = mix(inside, half4(0, 0, 0, 0), err);", outColor);
+        builder->codeAppendf("%s = mix(%s, half4(0, 0, 0, 0), err);", outColor, color.c_str());
     } else {
         // A simple look up
-        builder->codeAppendf("%s = inside;", outColor);
+        builder->codeAppendf("%s = %s;", outColor, color.c_str());
     }
 }
 
 void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
                                         const GrTextureDomain& textureDomain,
-                                        GrTextureProxy* proxy,
-                                        const GrSamplerState& sampler) {
-    GrTexture* tex = proxy->peekTexture();
-    SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
-    if (kIgnore_Mode != textureDomain.modeX() || kIgnore_Mode != textureDomain.modeY()) {
-        bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
-                             textureDomain.modeY() == kDecal_Mode;
+                                        const GrSurfaceProxy* proxy,
+                                        const GrSamplerState& state) {
+    // We want a hard transition from texture content to trans-black in nearest mode.
+    bool filterDecal = state.filter() != GrSamplerState::Filter::kNearest;
+    this->setData(pdman, textureDomain, proxy, filterDecal);
+}
 
-        // If the texture is using nearest filtering, then the decal filter weight should step from
-        // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
-        // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
-        // texture and transparent.
-        SkScalar decalFilterWeight = sampler.filter() == GrSamplerState::Filter::kNearest ?
-                SK_ScalarHalf : 1.0f;
+void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
+                                        const GrTextureDomain& textureDomain,
+                                        bool filterIfDecal) {
+    this->setData(pdman, textureDomain, nullptr, filterIfDecal);
+}
+
+void GrTextureDomain::GLDomain::setData(const GrGLSLProgramDataManager& pdman,
+                                        const GrTextureDomain& textureDomain,
+                                        const GrSurfaceProxy* proxy,
+                                        bool filterIfDecal) {
+    SkASSERT(fHasMode && textureDomain.modeX() == fModeX && textureDomain.modeY() == fModeY);
+    if (kIgnore_Mode == textureDomain.modeX() && kIgnore_Mode == textureDomain.modeY()) {
+        return;
+    }
+    // If the texture is using nearest filtering, then the decal filter weight should step from
+    // 0 (texture) to 1 (transparent) one half pixel away from the domain. When doing any other
+    // form of filtering, the weight should be 1.0 so that it smoothly interpolates between the
+    // texture and transparent.
+    // Start off assuming we're in pixel units and later adjust if we have to deal with normalized
+    // texture coords.
+    float decalFilterWeights[3] = {1.f, 1.f, filterIfDecal ? 1.f : 0.5f};
+    bool sendDecalData = textureDomain.modeX() == kDecal_Mode ||
+                         textureDomain.modeY() == kDecal_Mode;
+    float tempDomainValues[4];
+    const float* values;
+    if (proxy) {
         SkScalar wInv, hInv, h;
-        if (proxy->textureType() == GrTextureType::kRectangle) {
+        GrTexture* tex = proxy->peekTexture();
+        if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
             wInv = hInv = 1.f;
             h = tex->height();
-
-            // Don't do any scaling by texture size for decal filter rate, it's already in pixels
-            if (sendDecalData) {
-                pdman.set3f(fDecalUni, 1.f, 1.f, decalFilterWeight);
-            }
+            // Don't do any scaling by texture size for decal filter rate, it's already in
+            // pixels
         } else {
             wInv = SK_Scalar1 / tex->width();
             hInv = SK_Scalar1 / tex->height();
             h = 1.f;
 
-            if (sendDecalData) {
-                pdman.set3f(fDecalUni, tex->width(), tex->height(), decalFilterWeight);
-            }
+            // Account for texture coord normalization in decal filter weights.
+            decalFilterWeights[0] = tex->width();
+            decalFilterWeights[1] = tex->height();
         }
 
-        float values[kPrevDomainCount] = {
-            SkScalarToFloat(textureDomain.domain().fLeft * wInv),
-            SkScalarToFloat(textureDomain.domain().fTop * hInv),
-            SkScalarToFloat(textureDomain.domain().fRight * wInv),
-            SkScalarToFloat(textureDomain.domain().fBottom * hInv)
-        };
+        tempDomainValues[0] = SkScalarToFloat(textureDomain.domain().fLeft * wInv);
+        tempDomainValues[1] = SkScalarToFloat(textureDomain.domain().fTop * hInv);
+        tempDomainValues[2] = SkScalarToFloat(textureDomain.domain().fRight * wInv);
+        tempDomainValues[3] = SkScalarToFloat(textureDomain.domain().fBottom * hInv);
 
-        if (proxy->textureType() == GrTextureType::kRectangle) {
-            SkASSERT(values[0] >= 0.0f && values[0] <= proxy->width());
-            SkASSERT(values[1] >= 0.0f && values[1] <= proxy->height());
-            SkASSERT(values[2] >= 0.0f && values[2] <= proxy->width());
-            SkASSERT(values[3] >= 0.0f && values[3] <= proxy->height());
+        if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
+            SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= proxy->width());
+            SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= proxy->height());
+            SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= proxy->width());
+            SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= proxy->height());
         } else {
-            SkASSERT(values[0] >= 0.0f && values[0] <= 1.0f);
-            SkASSERT(values[1] >= 0.0f && values[1] <= 1.0f);
-            SkASSERT(values[2] >= 0.0f && values[2] <= 1.0f);
-            SkASSERT(values[3] >= 0.0f && values[3] <= 1.0f);
+            SkASSERT(tempDomainValues[0] >= 0.0f && tempDomainValues[0] <= 1.0f);
+            SkASSERT(tempDomainValues[1] >= 0.0f && tempDomainValues[1] <= 1.0f);
+            SkASSERT(tempDomainValues[2] >= 0.0f && tempDomainValues[2] <= 1.0f);
+            SkASSERT(tempDomainValues[3] >= 0.0f && tempDomainValues[3] <= 1.0f);
         }
 
         // vertical flip if necessary
         if (kBottomLeft_GrSurfaceOrigin == proxy->origin()) {
-            values[1] = h - values[1];
-            values[3] = h - values[3];
+            tempDomainValues[1] = h - tempDomainValues[1];
+            tempDomainValues[3] = h - tempDomainValues[3];
 
             // The top and bottom were just flipped, so correct the ordering
             // of elements so that values = (l, t, r, b).
             using std::swap;
-            swap(values[1], values[3]);
+            swap(tempDomainValues[1], tempDomainValues[3]);
         }
-        if (0 != memcmp(values, fPrevDomain, kPrevDomainCount * sizeof(float))) {
-            pdman.set4fv(fDomainUni, 1, values);
-            memcpy(fPrevDomain, values, kPrevDomainCount * sizeof(float));
-        }
+        values = tempDomainValues;
+    } else {
+        values = textureDomain.domain().asScalars();
+    }
+    if (!std::equal(values, values + 4, fPrevDomain)) {
+        pdman.set4fv(fDomainUni, 1, values);
+        std::copy_n(values, 4, fPrevDomain);
+    }
+    if (sendDecalData &&
+        !std::equal(decalFilterWeights, decalFilterWeights + 3, fPrevDeclFilterWeights)) {
+        pdman.set3fv(fDecalUni, 1, decalFilterWeights);
+        std::copy_n(decalFilterWeights, 3, fPrevDeclFilterWeights);
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
-        sk_sp<GrTextureProxy> proxy,
-        GrColorType srcColorType,
-        const SkMatrix& matrix,
-        const SkRect& domain,
-        GrTextureDomain::Mode mode,
-        GrSamplerState::Filter filterMode) {
-    return Make(std::move(proxy), srcColorType, matrix, domain, mode, mode,
-                GrSamplerState(GrSamplerState::WrapMode::kClamp, filterMode));
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode mode,
+                                                          bool decalIsFiltered) {
+    return Make(std::move(fp), domain, mode, mode, decalIsFiltered);
 }
 
-std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::Make(
-        sk_sp<GrTextureProxy> proxy,
-        GrColorType srcColorType,
-        const SkMatrix& matrix,
-        const SkRect& domain,
-        GrTextureDomain::Mode modeX,
-        GrTextureDomain::Mode modeY,
-        const GrSamplerState& sampler) {
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode modeX,
+                                                          GrTextureDomain::Mode modeY,
+                                                          bool decalIsFiltered) {
+    if (modeX == GrTextureDomain::kIgnore_Mode && modeY == GrTextureDomain::kIgnore_Mode) {
+        return fp;
+    }
+    int count = 0;
+    GrCoordTransform* coordTransform = nullptr;
+    for (auto [transform, ignored] : GrFragmentProcessor::FPCoordTransformRange(*fp)) {
+        ++count;
+        coordTransform = &transform;
+    }
+    // If there are no coord transforms on the passed FP or it's children then there's no need to
+    // enforce a domain.
+    // We have a limitation that only one coord transform is support when overriding local coords.
+    // If that limit were relaxed we would need to add a coord transform for each descendent FP
+    // transform and possibly have multiple domain rects to account for different proxy
+    // normalization and y-reversals.
+    if (count != 1) {
+        return fp;
+    }
+    GrCoordTransform transformCopy = *coordTransform;
+    // Reset the child FP's coord transform.
+    *coordTransform = {};
     // If both domain modes happen to be ignore, it would be faster to just drop the domain logic
-    // entirely Technically, we could also use the simple texture effect if the domain modes agree
-    // with the sampler modes and the proxy is the same size as the domain. It's a lot easier for
-    // calling code to detect these cases and handle it themselves.
-    return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(
-            std::move(proxy), srcColorType, matrix, domain, modeX, modeY, sampler));
+    // entirely and return the original FP. We'd need a GrMatrixProcessor if the matrix is not
+    // identity, though.
+    return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(
+            std::move(fp), transformCopy, domain, modeX, modeY, decalIsFiltered));
 }
 
-GrTextureDomainEffect::GrTextureDomainEffect(sk_sp<GrTextureProxy> proxy,
-                                             GrColorType srcColorType,
-                                             const SkMatrix& matrix,
-                                             const SkRect& domain,
-                                             GrTextureDomain::Mode modeX,
-                                             GrTextureDomain::Mode modeY,
-                                             const GrSamplerState& sampler)
-        : INHERITED(kGrTextureDomainEffect_ClassID,
-                    ModulateForSamplerOptFlags(srcColorType,
-                            GrTextureDomain::IsDecalSampled(sampler, modeX, modeY)))
-        , fCoordTransform(matrix, proxy.get())
-        , fTextureDomain(proxy.get(), domain, modeX, modeY)
-        , fTextureSampler(std::move(proxy), sampler) {
-    SkASSERT((modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode) ||
-             sampler.filter() == GrSamplerState::Filter::kNearest);
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode mode,
+                                                          GrSamplerState::Filter filter) {
+    bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
+    return Make(std::move(fp), domain, mode, filterIfDecal);
+}
+
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::Make(std::unique_ptr<GrFragmentProcessor> fp,
+                                                          const SkRect& domain,
+                                                          GrTextureDomain::Mode modeX,
+                                                          GrTextureDomain::Mode modeY,
+                                                          GrSamplerState::Filter filter) {
+    bool filterIfDecal = filter != GrSamplerState::Filter::kNearest;
+    return Make(std::move(fp), domain, modeX, modeY, filterIfDecal);
+}
+GrFragmentProcessor::OptimizationFlags GrDomainEffect::Flags(GrFragmentProcessor* fp,
+                                                             GrTextureDomain::Mode modeX,
+                                                             GrTextureDomain::Mode modeY) {
+    auto fpFlags = GrFragmentProcessor::ProcessorOptimizationFlags(fp);
+    if (modeX == GrTextureDomain::kDecal_Mode || modeY == GrTextureDomain::kDecal_Mode) {
+        return fpFlags & ~kPreservesOpaqueInput_OptimizationFlag;
+    }
+    return fpFlags;
+}
+
+GrDomainEffect::GrDomainEffect(std::unique_ptr<GrFragmentProcessor> fp,
+                               const GrCoordTransform& coordTransform,
+                               const SkRect& domain,
+                               GrTextureDomain::Mode modeX,
+                               GrTextureDomain::Mode modeY,
+                               bool decalIsFiltered)
+        : INHERITED(kGrDomainEffect_ClassID, Flags(fp.get(), modeX, modeY))
+        , fCoordTransform(coordTransform)
+        , fDomain(domain, modeX, modeY)
+        , fDecalIsFiltered(decalIsFiltered) {
+    SkASSERT(fp);
+    fp->setSampledWithExplicitCoords(true);
+    this->registerChildProcessor(std::move(fp));
     this->addCoordTransform(&fCoordTransform);
-    this->setTextureSamplerCnt(1);
+    if (fDomain.modeX() != GrTextureDomain::kDecal_Mode &&
+        fDomain.modeY() != GrTextureDomain::kDecal_Mode) {
+        // Canonicalize this don't care value so we don't have to worry about it elsewhere.
+        fDecalIsFiltered = false;
+    }
 }
 
-GrTextureDomainEffect::GrTextureDomainEffect(const GrTextureDomainEffect& that)
-        : INHERITED(kGrTextureDomainEffect_ClassID, that.optimizationFlags())
+GrDomainEffect::GrDomainEffect(const GrDomainEffect& that)
+        : INHERITED(kGrDomainEffect_ClassID, that.optimizationFlags())
         , fCoordTransform(that.fCoordTransform)
-        , fTextureDomain(that.fTextureDomain)
-        , fTextureSampler(that.fTextureSampler) {
+        , fDomain(that.fDomain)
+        , fDecalIsFiltered(that.fDecalIsFiltered) {
+    auto child = that.childProcessor(0).clone();
+    child->setSampledWithExplicitCoords(true);
+    this->registerChildProcessor(std::move(child));
     this->addCoordTransform(&fCoordTransform);
-    this->setTextureSamplerCnt(1);
 }
 
-void GrTextureDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
-                                                  GrProcessorKeyBuilder* b) const {
-    b->add32(GrTextureDomain::GLDomain::DomainKey(fTextureDomain));
+void GrDomainEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+                                           GrProcessorKeyBuilder* b) const {
+    b->add32(GrTextureDomain::GLDomain::DomainKey(fDomain));
 }
 
-GrGLSLFragmentProcessor* GrTextureDomainEffect::onCreateGLSLInstance() const  {
+GrGLSLFragmentProcessor* GrDomainEffect::onCreateGLSLInstance() const {
     class GLSLProcessor : public GrGLSLFragmentProcessor {
     public:
         void emitCode(EmitArgs& args) override {
-            const GrTextureDomainEffect& tde = args.fFp.cast<GrTextureDomainEffect>();
-            const GrTextureDomain& domain = tde.fTextureDomain;
+            const GrDomainEffect& de = args.fFp.cast<GrDomainEffect>();
+            const GrTextureDomain& domain = de.fDomain;
 
-            GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
             SkString coords2D =
-                              fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
+                    args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
 
-            fGLDomain.sampleTexture(fragBuilder,
-                                    args.fUniformHandler,
-                                    args.fShaderCaps,
-                                    domain,
-                                    args.fOutputColor,
-                                    coords2D,
-                                    args.fTexSamplers[0],
-                                    args.fInputColor);
+            fGLDomain.sampleProcessor(domain, args.fInputColor, args.fOutputColor, coords2D, this,
+                                      args, 0);
         }
 
     protected:
         void onSetData(const GrGLSLProgramDataManager& pdman,
                        const GrFragmentProcessor& fp) override {
-            const GrTextureDomainEffect& tde = fp.cast<GrTextureDomainEffect>();
-            const GrTextureDomain& domain = tde.fTextureDomain;
-            GrTextureProxy* proxy = tde.textureSampler(0).proxy();
-
-            fGLDomain.setData(pdman, domain, proxy, tde.textureSampler(0).samplerState());
+            const GrDomainEffect& de = fp.cast<GrDomainEffect>();
+            const GrTextureDomain& domain = de.fDomain;
+            fGLDomain.setData(pdman, domain, de.fCoordTransform.proxy(), de.fDecalIsFiltered);
         }
 
     private:
@@ -348,53 +439,63 @@
     return new GLSLProcessor;
 }
 
-bool GrTextureDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
-    const GrTextureDomainEffect& s = sBase.cast<GrTextureDomainEffect>();
-    return this->fTextureDomain == s.fTextureDomain;
+bool GrDomainEffect::onIsEqual(const GrFragmentProcessor& sBase) const {
+    auto& td = sBase.cast<GrDomainEffect>();
+    return fDomain == td.fDomain && fDecalIsFiltered == td.fDecalIsFiltered;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTextureDomainEffect);
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDomainEffect);
 
 #if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> GrTextureDomainEffect::TestCreate(GrProcessorTestData* d) {
-    int texIdx = d->fRandom->nextBool() ? GrProcessorUnitTest::kSkiaPMTextureIdx
-                                        : GrProcessorUnitTest::kAlphaTextureIdx;
-    sk_sp<GrTextureProxy> proxy = d->textureProxy(texIdx);
-    SkRect domain;
-    domain.fLeft = d->fRandom->nextRangeScalar(0, proxy->width());
-    domain.fRight = d->fRandom->nextRangeScalar(domain.fLeft, proxy->width());
-    domain.fTop = d->fRandom->nextRangeScalar(0, proxy->height());
-    domain.fBottom = d->fRandom->nextRangeScalar(domain.fTop, proxy->height());
-    GrTextureDomain::Mode modeX =
-        (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
-    GrTextureDomain::Mode modeY =
-        (GrTextureDomain::Mode) d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
-    const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
-    bool bilerp = modeX != GrTextureDomain::kRepeat_Mode && modeY != GrTextureDomain::kRepeat_Mode ?
-            d->fRandom->nextBool() : false;
-    return GrTextureDomainEffect::Make(
-            std::move(proxy),
-            d->textureProxyColorType(texIdx),
-            matrix,
-            domain,
-            modeX,
-            modeY,
-            GrSamplerState(GrSamplerState::WrapMode::kClamp, bilerp ?
-                           GrSamplerState::Filter::kBilerp : GrSamplerState::Filter::kNearest));
+std::unique_ptr<GrFragmentProcessor> GrDomainEffect::TestCreate(GrProcessorTestData* d) {
+    do {
+        GrTextureDomain::Mode modeX =
+                (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
+        GrTextureDomain::Mode modeY =
+                (GrTextureDomain::Mode)d->fRandom->nextULessThan(GrTextureDomain::kModeCount);
+        auto child = GrProcessorUnitTest::MakeChildFP(d);
+        const auto* childPtr = child.get();
+        SkRect domain;
+        // We assert if the child's coord transform has a proxy and the domain rect is outside its
+        // bounds.
+        GrFragmentProcessor::CoordTransformIter ctIter(*child);
+        if (!ctIter) {
+            continue;
+        }
+        auto [transform, fp] = *ctIter;
+        if (auto proxy = transform.proxy()) {
+            auto [w, h] = proxy->backingStoreDimensions();
+            domain.fLeft   = d->fRandom->nextRangeScalar(0, w);
+            domain.fRight  = d->fRandom->nextRangeScalar(0, w);
+            domain.fTop    = d->fRandom->nextRangeScalar(0, h);
+            domain.fBottom = d->fRandom->nextRangeScalar(0, h);
+        } else {
+            domain.fLeft   = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fRight  = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fTop    = d->fRandom->nextRangeScalar(-100.f, 100.f);
+            domain.fBottom = d->fRandom->nextRangeScalar(-100.f, 100.f);
+        }
+        domain.sort();
+        bool filterIfDecal = d->fRandom->nextBool();
+        auto result = GrDomainEffect::Make(std::move(child), domain, modeX, modeY, filterIfDecal);
+        if (result && result.get() != childPtr) {
+            return result;
+        }
+    } while (true);
 }
 #endif
 
 ///////////////////////////////////////////////////////////////////////////////
 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceTextureDecalFragmentProcessor::Make(
-        sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
+        sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset) {
     return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceTextureDecalFragmentProcessor(
             std::move(proxy), subset, deviceSpaceOffset));
 }
 
 GrDeviceSpaceTextureDecalFragmentProcessor::GrDeviceSpaceTextureDecalFragmentProcessor(
-        sk_sp<GrTextureProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
+        sk_sp<GrSurfaceProxy> proxy, const SkIRect& subset, const SkIPoint& deviceSpaceOffset)
         : INHERITED(kGrDeviceSpaceTextureDecalFragmentProcessor_ClassID,
                     kCompatibleWithCoverageAsAlpha_OptimizationFlag)
         , fTextureSampler(proxy, GrSamplerState::ClampNearest())
@@ -449,13 +550,13 @@
                        const GrFragmentProcessor& fp) override {
             const GrDeviceSpaceTextureDecalFragmentProcessor& dstdfp =
                     fp.cast<GrDeviceSpaceTextureDecalFragmentProcessor>();
-            GrTextureProxy* proxy = dstdfp.textureSampler(0).proxy();
-            GrTexture* texture = proxy->peekTexture();
+            GrSurfaceProxy* proxy = dstdfp.textureSampler(0).proxy();
+            SkISize textureDims = proxy->backingStoreDimensions();
 
             fGLDomain.setData(pdman, dstdfp.fTextureDomain, proxy,
                               dstdfp.textureSampler(0).samplerState());
-            float iw = 1.f / texture->width();
-            float ih = 1.f / texture->height();
+            float iw = 1.f / textureDims.width();
+            float ih = 1.f / textureDims.height();
             float scaleAndTransData[4] = {
                 iw, ih,
                 -dstdfp.fDeviceSpaceOffset.fX * iw, -dstdfp.fDeviceSpaceOffset.fY * ih
diff --git a/src/gpu/effects/GrTextureDomain.h b/src/gpu/effects/GrTextureDomain.h
index 96c2d20..073a15b 100644
--- a/src/gpu/effects/GrTextureDomain.h
+++ b/src/gpu/effects/GrTextureDomain.h
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef GrTextureDomainEffect_DEFINED
-#define GrTextureDomainEffect_DEFINED
+#ifndef GrTextureDomain_DEFINED
+#define GrTextureDomain_DEFINED
 
 #include "src/gpu/GrCoordTransform.h"
 #include "src/gpu/GrFragmentProcessor.h"
@@ -50,10 +50,20 @@
     }
 
     /**
+     * Construct a domain used to sample a GrFragmentProcessor.
+     *
      * @param index     Pass a value >= 0 if using multiple texture domains in the same effect.
      *                  It is used to keep inserted variables from causing name collisions.
      */
-    GrTextureDomain(GrTextureProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1);
+    GrTextureDomain(const SkRect& domain, Mode modeX, Mode modeY, int index = -1);
+
+    /**
+     * Construct a domain used to directly sampler a texture.
+     *
+     * @param index     Pass a value >= 0 if using multiple texture domains in the same effect.
+     *                  It is used to keep inserted variables from causing name collisions.
+     */
+    GrTextureDomain(GrSurfaceProxy*, const SkRect& domain, Mode modeX, Mode modeY, int index = -1);
 
     GrTextureDomain(const GrTextureDomain&) = default;
 
@@ -104,6 +114,7 @@
                (kIgnore_Mode == fModeY || (fDomain.fTop == that.fDomain.fTop &&
                                            fDomain.fBottom == that.fDomain.fBottom));
     }
+    bool operator!=(const GrTextureDomain& that) const { return !(*this == that); }
 
     /**
      * A GrGLSLFragmentProcessor subclass that corresponds to a GrProcessor subclass that uses
@@ -113,19 +124,32 @@
      */
     class GLDomain {
     public:
-        GLDomain() {
-            for (int i = 0; i < kPrevDomainCount; i++) {
-                fPrevDomain[i] = SK_FloatNaN;
-            }
-        }
+        GLDomain() = default;
 
         /**
-         * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture W.R.T. the
+         * Call this from GrGLSLFragmentProcessor::emitCode() to sample a child processor WRT the
          * domain and mode.
          *
          * @param outcolor  name of half4 variable to hold the sampled color.
-         * @param inCoords  name of float2 variable containing the coords to be used with the domain.
-         *                  It is assumed that this is a variable and not an expression.
+         * @param inCoords  name of float2 variable containing the coords to be used with the
+         *                  domain.
+         * @param inColor   color passed to the child processor.
+         */
+        void sampleProcessor(const GrTextureDomain& textureDomain,
+                             const char* inColor,
+                             const char* outColor,
+                             const SkString& inCoords,
+                             GrGLSLFragmentProcessor* parent,
+                             GrGLSLFragmentProcessor::EmitArgs& args,
+                             int childIndex);
+
+        /**
+         * Call this from GrGLSLFragmentProcessor::emitCode() to sample the texture WRT the domain
+         * and mode.
+         *
+         * @param outcolor  name of half4 variable to hold the sampled color.
+         * @param inCoords  name of float2 variable containing the coords to be used with the
+         *                  domain.
          * @param inModulateColor   if non-nullptr the sampled color will be modulated with this
          *                          expression before being written to outColor.
          */
@@ -140,11 +164,23 @@
 
         /**
          * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
-         * texture domain. The rectangle is automatically adjusted to account for the texture's
-         * origin.
+         * domain. 'filterIfDecal' determines whether the transition to transparent black at the
+         * edge of domain is linearly interpolated over a unit interval or is "hard" when
+         * kDecal_Mode is used.
          */
-        void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, GrTextureProxy*,
-                     const GrSamplerState& sampler);
+        void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, bool filterIfDecal);
+
+        /**
+         * Call this from GrGLSLFragmentProcessor::setData() to upload uniforms necessary for the
+         * texture domain used with a texture proxy. The rectangle is automatically adjusted to
+         * account for the texture's origin. Filtering at the edge of the domain is inferred from
+         * the GrSamplerState's filter mode.
+         */
+        void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, const GrSurfaceProxy*,
+                     const GrSamplerState& state);
+        /** Same as above but with direct control over decal filtering. */
+        void setData(const GrGLSLProgramDataManager&, const GrTextureDomain&, const GrSurfaceProxy*,
+                     bool filterIfDecal);
 
         enum {
             kModeBits = 2, // See DomainKey().
@@ -161,7 +197,17 @@
         }
 
     private:
-        static const int kPrevDomainCount = 4;
+        // Takes a builder and a coord and appends to the builder a string that is an expression
+        // the evaluates to a half4 color.
+        using AppendSample = SkString(const char* coord);
+
+        void sample(GrGLSLShaderBuilder* builder,
+                    GrGLSLUniformHandler* uniformHandler,
+                    const GrTextureDomain& textureDomain,
+                    const char* outColor,
+                    const SkString& inCoords,
+                    const std::function<AppendSample>& color);
+
         SkDEBUGCODE(Mode                        fModeX;)
         SkDEBUGCODE(Mode                        fModeY;)
         SkDEBUGCODE(bool                        fHasMode = false;)
@@ -172,67 +218,84 @@
         GrGLSLProgramDataManager::UniformHandle fDecalUni;
         SkString                                fDecalName;
 
-        float                                   fPrevDomain[kPrevDomainCount];
+        float                                   fPrevDomain[4] = {SK_FloatNaN};
+        float                                   fPrevDeclFilterWeights[3] = {SK_FloatNaN};
     };
 
 protected:
+    SkRect  fDomain;
     Mode    fModeX;
     Mode    fModeY;
-    SkRect  fDomain;
     int     fIndex;
 };
 
 /**
- * A basic texture effect that uses GrTextureDomain.
+ * This effect applies a domain rectangle with an edge "mode" to the result of the child FP's coord
+ * transform. Currently the passed FP (including its descendants) must have exactly 1 coord
+ * transform (due to internal program builder restrictions). Also, it's important to note that the
+ * domain rectangle is applied  AFTER the corod transform. This allows us to continue to lift the
+ * coord transform to the vertex shader. It might make this nicer for some use cases to add a
+ * pre-coord transform option and try to adjust the domain rect internally to convert to
+ * post-coord transform and keep everything in the vertex shader for simple use cases.
  */
-class GrTextureDomainEffect : public GrFragmentProcessor {
+class GrDomainEffect : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
-                                                     GrColorType srcColorType,
-                                                     const SkMatrix&,
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
                                                      const SkRect& domain,
-                                                     GrTextureDomain::Mode mode,
-                                                     GrSamplerState::Filter filterMode);
+                                                     GrTextureDomain::Mode,
+                                                     bool decalIsFiltered);
 
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
-                                                     GrColorType srcColorType,
-                                                     const SkMatrix&,
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
                                                      const SkRect& domain,
                                                      GrTextureDomain::Mode modeX,
                                                      GrTextureDomain::Mode modeY,
-                                                     const GrSamplerState& sampler);
+                                                     bool decalIsFiltered);
 
-    const char* name() const override { return "TextureDomain"; }
+    // These variants infer decalIsFiltered from the Filter mode (true if not kNearest).
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode,
+                                                     GrSamplerState::Filter);
+
+    static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor>,
+                                                     const SkRect& domain,
+                                                     GrTextureDomain::Mode modeX,
+                                                     GrTextureDomain::Mode modeY,
+                                                     GrSamplerState::Filter);
+
+    const char* name() const override { return "Domain"; }
 
     std::unique_ptr<GrFragmentProcessor> clone() const override {
-        return std::unique_ptr<GrFragmentProcessor>(new GrTextureDomainEffect(*this));
+        return std::unique_ptr<GrFragmentProcessor>(new GrDomainEffect(*this));
     }
 
 #ifdef SK_DEBUG
     SkString dumpInfo() const override {
         SkString str;
-        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f]",
-                    fTextureDomain.domain().fLeft, fTextureDomain.domain().fTop,
-                    fTextureDomain.domain().fRight, fTextureDomain.domain().fBottom);
+        str.appendf("Domain: [L: %.2f, T: %.2f, R: %.2f, B: %.2f], filterDecal: %d",
+                    fDomain.domain().fLeft, fDomain.domain().fTop, fDomain.domain().fRight,
+                    fDomain.domain().fBottom, fDecalIsFiltered);
         str.append(INHERITED::dumpInfo());
         return str;
     }
 #endif
 
 private:
+    GrFragmentProcessor::OptimizationFlags Flags(GrFragmentProcessor*, GrTextureDomain::Mode,
+                                                 GrTextureDomain::Mode);
+
     GrCoordTransform fCoordTransform;
-    GrTextureDomain fTextureDomain;
-    TextureSampler fTextureSampler;
+    GrTextureDomain fDomain;
+    bool fDecalIsFiltered;
 
-    GrTextureDomainEffect(sk_sp<GrTextureProxy>,
-                          GrColorType srcColorType,
-                          const SkMatrix&,
-                          const SkRect& domain,
-                          GrTextureDomain::Mode modeX,
-                          GrTextureDomain::Mode modeY,
-                          const GrSamplerState&);
+    GrDomainEffect(std::unique_ptr<GrFragmentProcessor>,
+                   const GrCoordTransform& transform,
+                   const SkRect& domain,
+                   GrTextureDomain::Mode modeX,
+                   GrTextureDomain::Mode modeY,
+                   bool decalIsFiltered);
 
-    explicit GrTextureDomainEffect(const GrTextureDomainEffect&);
+    explicit GrDomainEffect(const GrDomainEffect&);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
 
@@ -240,8 +303,6 @@
 
     bool onIsEqual(const GrFragmentProcessor&) const override;
 
-    const TextureSampler& onTextureSampler(int) const override { return fTextureSampler; }
-
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
 
     typedef GrFragmentProcessor INHERITED;
@@ -249,7 +310,7 @@
 
 class GrDeviceSpaceTextureDecalFragmentProcessor : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy>,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy>,
                                                      const SkIRect& subset,
                                                      const SkIPoint& deviceSpaceOffset);
 
@@ -274,7 +335,7 @@
     GrTextureDomain fTextureDomain;
     SkIPoint fDeviceSpaceOffset;
 
-    GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrTextureProxy>,
+    GrDeviceSpaceTextureDecalFragmentProcessor(sk_sp<GrSurfaceProxy>,
                                                const SkIRect&, const SkIPoint&);
     GrDeviceSpaceTextureDecalFragmentProcessor(const GrDeviceSpaceTextureDecalFragmentProcessor&);
 
diff --git a/src/gpu/effects/GrYUVtoRGBEffect.cpp b/src/gpu/effects/GrYUVtoRGBEffect.cpp
index 0de89f1..865bcde 100644
--- a/src/gpu/effects/GrYUVtoRGBEffect.cpp
+++ b/src/gpu/effects/GrYUVtoRGBEffect.cpp
@@ -8,33 +8,13 @@
 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
 
 #include "include/gpu/GrTexture.h"
+#include "src/core/SkYUVMath.h"
 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/sksl/SkSLCPP.h"
 #include "src/sksl/SkSLUtil.h"
 
-static const float kJPEGConversionMatrix[16] = {
-    1.0f,  0.0f,       1.402f,    -0.703749f,
-    1.0f, -0.344136f, -0.714136f,  0.531211f,
-    1.0f,  1.772f,     0.0f,      -0.889475f,
-    0.0f,  0.0f,       0.0f,       1.0
-};
-
-static const float kRec601ConversionMatrix[16] = {
-    1.164f,  0.0f,    1.596f, -0.87075f,
-    1.164f, -0.391f, -0.813f,  0.52925f,
-    1.164f,  2.018f,  0.0f,   -1.08175f,
-    0.0f,    0.0f,    0.0f,    1.0
-};
-
-static const float kRec709ConversionMatrix[16] = {
-    1.164f,  0.0f,    1.793f, -0.96925f,
-    1.164f, -0.213f, -0.533f,  0.30025f,
-    1.164f,  2.112f,  0.0f,   -1.12875f,
-    0.0f,    0.0f,    0.0f,    1.0f
-};
-
 std::unique_ptr<GrFragmentProcessor> GrYUVtoRGBEffect::Make(const sk_sp<GrTextureProxy> proxies[],
                                                             const SkYUVAIndex yuvaIndices[4],
                                                             SkYUVColorSpace yuvColorSpace,
@@ -122,6 +102,7 @@
                 SkASSERT(fColorSpaceMatrixVar.isValid());
                 fragBuilder->codeAppendf(
                     "yuvOne *= %s;", args.fUniformHandler->getUniformCStr(fColorSpaceMatrixVar));
+                fragBuilder->codeAppend("yuvOne.xyz = clamp(yuvOne.xyz, 0, 1);");
             }
 
             if (_outer.yuvaIndex(3).fIndex >= 0) {
@@ -142,21 +123,18 @@
                        const GrFragmentProcessor& _proc) override {
             const GrYUVtoRGBEffect& _outer = _proc.cast<GrYUVtoRGBEffect>();
 
-            switch (_outer.yuvColorSpace()) {
-                case kJPEG_SkYUVColorSpace:
-                    SkASSERT(fColorSpaceMatrixVar.isValid());
-                    pdman.setMatrix4f(fColorSpaceMatrixVar, kJPEGConversionMatrix);
-                    break;
-                case kRec601_SkYUVColorSpace:
-                    SkASSERT(fColorSpaceMatrixVar.isValid());
-                    pdman.setMatrix4f(fColorSpaceMatrixVar, kRec601ConversionMatrix);
-                    break;
-                case kRec709_SkYUVColorSpace:
-                    SkASSERT(fColorSpaceMatrixVar.isValid());
-                    pdman.setMatrix4f(fColorSpaceMatrixVar, kRec709ConversionMatrix);
-                    break;
-                case kIdentity_SkYUVColorSpace:
-                    break;
+            if (_outer.yuvColorSpace() != kIdentity_SkYUVColorSpace) {
+                SkASSERT(fColorSpaceMatrixVar.isValid());
+                float yuvM[20];
+                SkColorMatrix_YUV2RGB(_outer.yuvColorSpace(), yuvM);
+                // Need to drop the fourth column to go to 4x4
+                float mtx[16] = {
+                    yuvM[ 0], yuvM[ 1], yuvM[ 2], yuvM[ 4],
+                    yuvM[ 5], yuvM[ 6], yuvM[ 7], yuvM[ 9],
+                    yuvM[10], yuvM[11], yuvM[12], yuvM[14],
+                    yuvM[15], yuvM[16], yuvM[17], yuvM[19],
+                };
+                pdman.setMatrix4f(fColorSpaceMatrixVar, mtx);
             }
 
             int numSamplers = _outer.numTextureSamplers();
diff --git a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
index 506cedb..ffcaca1 100644
--- a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
@@ -51,9 +51,11 @@
                 "color.w = %s;\n}\n%s = color;\n",
                 args.fInputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                sk_TransformedCoords2D_0.c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 args.fUniformHandler->getUniformCStr(outerThresholdVar),
                 args.fUniformHandler->getUniformCStr(outerThresholdVar),
                 args.fUniformHandler->getUniformCStr(outerThresholdVar),
diff --git a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
index 730054b..112e72a 100644
--- a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
@@ -18,7 +18,7 @@
 public:
     inline OptimizationFlags optFlags(float outerThreshold);
 
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> mask,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> mask,
                                                      float innerThreshold,
                                                      float outerThreshold,
                                                      const SkIRect& bounds) {
@@ -34,7 +34,7 @@
     float outerThreshold;
 
 private:
-    GrAlphaThresholdFragmentProcessor(sk_sp<GrTextureProxy> mask, float innerThreshold,
+    GrAlphaThresholdFragmentProcessor(sk_sp<GrSurfaceProxy> mask, float innerThreshold,
                                       float outerThreshold, const SkIRect& bounds)
             : INHERITED(kGrAlphaThresholdFragmentProcessor_ClassID, kNone_OptimizationFlags)
             , maskCoordTransform(
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
index ccef347..3d5a925 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
@@ -284,7 +284,10 @@
                 args.fUniformHandler->getUniformCStr(circleDataVar), args.fOutputColor,
                 args.fInputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str());
     }
 
 private:
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
index 93d2403..a3221ec 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
@@ -28,7 +28,7 @@
 
 private:
     GrCircleBlurFragmentProcessor(SkRect circleRect, float textureRadius, float solidRadius,
-                                  sk_sp<GrTextureProxy> blurProfileSampler)
+                                  sk_sp<GrSurfaceProxy> blurProfileSampler)
             : INHERITED(kGrCircleBlurFragmentProcessor_ClassID,
                         (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
             , circleRect(circleRect)
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.h b/src/gpu/effects/generated/GrConfigConversionEffect.h
index acbc4cc..a7825c3 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.h
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.h
@@ -68,7 +68,6 @@
         // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
         // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
         sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
-        GrColorType dataColorType = SkColorTypeToGrColorType(image->colorType());
         sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(
                 std::move(image), 1, SkBudgeted::kYes, SkBackingFit::kExact);
         if (!dataProxy) {
@@ -89,7 +88,7 @@
         std::unique_ptr<GrFragmentProcessor> upmToPM(
                 new GrConfigConversionEffect(PMConversion::kToPremul));
 
-        paint1.addColorTextureProcessor(dataProxy, dataColorType, SkMatrix::I());
+        paint1.addColorTextureProcessor(dataProxy, kPremul_SkAlphaType, SkMatrix::I());
         paint1.addColorFragmentProcessor(pmToUPM->clone());
         paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
@@ -103,14 +102,16 @@
         // draw
         tempRTC->discard();
 
-        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), kColorType, SkMatrix::I());
+        paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), kUnpremul_SkAlphaType,
+                                        SkMatrix::I());
         paint2.addColorFragmentProcessor(std::move(upmToPM));
         paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
         tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect,
                                 kRect);
 
-        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), kColorType, SkMatrix::I());
+        paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), kPremul_SkAlphaType,
+                                        SkMatrix::I());
         paint3.addColorFragmentProcessor(std::move(pmToUPM));
         paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
diff --git a/src/gpu/effects/generated/GrMagnifierEffect.cpp b/src/gpu/effects/generated/GrMagnifierEffect.cpp
index 4f7a2ea..5a29834 100644
--- a/src/gpu/effects/generated/GrMagnifierEffect.cpp
+++ b/src/gpu/effects/generated/GrMagnifierEffect.cpp
@@ -57,8 +57,7 @@
                 "- delta;\n    float dist = length(delta);\n    dist = max(2.0 - dist, 0.0);\n    "
                 "weight = min(dist * dist, 1.0);\n} else {\n    float2 delta_squared = delta * "
                 "delta;\n    weight = min(min(delta_squared.x, delta_square",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
+                sk_TransformedCoords2D_0.c_str(),
                 args.fUniformHandler->getUniformCStr(offsetVar),
                 args.fUniformHandler->getUniformCStr(xInvZoomVar),
                 args.fUniformHandler->getUniformCStr(yInvZoomVar),
@@ -70,7 +69,10 @@
                 "d.y), 1.0);\n}\n%s = sample(%s, mix(coord, zoom_coord, weight)).%s;\n",
                 args.fOutputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str());
     }
 
 private:
diff --git a/src/gpu/effects/generated/GrMagnifierEffect.h b/src/gpu/effects/generated/GrMagnifierEffect.h
index cb0d005..c7e3013 100644
--- a/src/gpu/effects/generated/GrMagnifierEffect.h
+++ b/src/gpu/effects/generated/GrMagnifierEffect.h
@@ -16,7 +16,7 @@
 #include "src/gpu/GrFragmentProcessor.h"
 class GrMagnifierEffect : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> src, SkIRect bounds,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> src, SkIRect bounds,
                                                      SkRect srcRect, float xInvZoom, float yInvZoom,
                                                      float xInvInset, float yInvInset) {
         return std::unique_ptr<GrFragmentProcessor>(new GrMagnifierEffect(
@@ -35,7 +35,7 @@
     float yInvInset;
 
 private:
-    GrMagnifierEffect(sk_sp<GrTextureProxy> src, SkIRect bounds, SkRect srcRect, float xInvZoom,
+    GrMagnifierEffect(sk_sp<GrSurfaceProxy> src, SkIRect bounds, SkRect srcRect, float xInvZoom,
                       float yInvZoom, float xInvInset, float yInvInset)
             : INHERITED(kGrMagnifierEffect_ClassID, kNone_OptimizationFlags)
             , srcCoordTransform(SkMatrix::I(), src.get())
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.cpp b/src/gpu/effects/generated/GrRRectBlurEffect.cpp
index 630c66c..35cf1fec 100644
--- a/src/gpu/effects/generated/GrRRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRRectBlurEffect.cpp
@@ -94,7 +94,10 @@
                 "sample(%s, float2(texCoord)).%s;\n",
                 args.fOutputColor, args.fInputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str());
     }
 
 private:
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.h b/src/gpu/effects/generated/GrRRectBlurEffect.h
index 9a3ff66..9aad281 100644
--- a/src/gpu/effects/generated/GrRRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRRectBlurEffect.h
@@ -117,7 +117,7 @@
 
 private:
     GrRRectBlurEffect(float sigma, SkRect rect, float cornerRadius,
-                      sk_sp<GrTextureProxy> ninePatchSampler)
+                      sk_sp<GrSurfaceProxy> ninePatchSampler)
             : INHERITED(kGrRRectBlurEffect_ClassID,
                         (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
             , sigma(sigma)
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.cpp b/src/gpu/effects/generated/GrRectBlurEffect.cpp
index 186dcfe..7643406 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRectBlurEffect.cpp
@@ -69,10 +69,16 @@
                 "sk_FragCoord.y);\n    } else {\n        l = half(sk_FragCoord.x - float(%s.x));\n "
                 "       r = half(float(%s.z) - sk_FragCoord.x);\n        t = half(sk_FragCoord.y - "
                 "float(%s.y));\n        b = half(float(",
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
                 args.fUniformHandler->getUniformCStr(invSixSigmaVar),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 args.fOutputColor, args.fInputColor,
                 rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
                 rectFVar.isValid() ? args.fUniformHandler->getUniformCStr(rectFVar) : "float4(0)",
@@ -93,13 +99,25 @@
                 args.fUniformHandler->getUniformCStr(invSixSigmaVar),
                 args.fUniformHandler->getUniformCStr(invSixSigmaVar),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str(),
                 args.fOutputColor, args.fInputColor);
     }
 
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.h b/src/gpu/effects/generated/GrRectBlurEffect.h
index b0c86bd..9b81d5f 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRectBlurEffect.h
@@ -116,7 +116,7 @@
     bool isFast;
 
 private:
-    GrRectBlurEffect(SkRect rect, sk_sp<GrTextureProxy> integral, float invSixSigma, bool isFast,
+    GrRectBlurEffect(SkRect rect, sk_sp<GrSurfaceProxy> integral, float invSixSigma, bool isFast,
                      GrSamplerState samplerParams)
             : INHERITED(kGrRectBlurEffect_ClassID,
                         (OptimizationFlags)kCompatibleWithCoverageAsAlpha_OptimizationFlag)
diff --git a/src/gpu/effects/generated/GrSimpleTextureEffect.cpp b/src/gpu/effects/generated/GrSimpleTextureEffect.cpp
index dd843b9..9c1ad14 100644
--- a/src/gpu/effects/generated/GrSimpleTextureEffect.cpp
+++ b/src/gpu/effects/generated/GrSimpleTextureEffect.cpp
@@ -30,9 +30,11 @@
         fragBuilder->codeAppendf(
                 "%s = %s * sample(%s, %s).%s;\n", args.fOutputColor, args.fInputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                sk_TransformedCoords2D_0.c_str(),
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str());
     }
 
 private:
@@ -86,7 +88,8 @@
                                              : GrSamplerState::Filter::kNearest);
 
     const SkMatrix& matrix = GrTest::TestMatrix(testData->fRandom);
-    return GrSimpleTextureEffect::Make(testData->textureProxy(texIdx),
-                                       testData->textureProxyColorType(texIdx), matrix, params);
+    auto alphaType = static_cast<SkAlphaType>(
+            testData->fRandom->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
+    return GrSimpleTextureEffect::Make(testData->textureProxy(texIdx), alphaType, matrix, params);
 }
 #endif
diff --git a/src/gpu/effects/generated/GrSimpleTextureEffect.h b/src/gpu/effects/generated/GrSimpleTextureEffect.h
index d70dd56..bdc11da 100644
--- a/src/gpu/effects/generated/GrSimpleTextureEffect.h
+++ b/src/gpu/effects/generated/GrSimpleTextureEffect.h
@@ -16,31 +16,31 @@
 #include "src/gpu/GrFragmentProcessor.h"
 class GrSimpleTextureEffect : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix) {
         return std::unique_ptr<GrFragmentProcessor>(
-                new GrSimpleTextureEffect(std::move(proxy), matrix, srcColorType,
+                new GrSimpleTextureEffect(std::move(proxy), matrix, alphaType,
                                           GrSamplerState(GrSamplerState::WrapMode::kClamp,
                                                          GrSamplerState::Filter::kNearest)));
     }
 
     /* clamp mode */
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix,
                                                      GrSamplerState::Filter filter) {
         return std::unique_ptr<GrFragmentProcessor>(new GrSimpleTextureEffect(
-                std::move(proxy), matrix, srcColorType,
+                std::move(proxy), matrix, alphaType,
                 GrSamplerState(GrSamplerState::WrapMode::kClamp, filter)));
     }
 
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
-                                                     GrColorType srcColorType,
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> proxy,
+                                                     SkAlphaType alphaType,
                                                      const SkMatrix& matrix,
                                                      const GrSamplerState& p) {
         return std::unique_ptr<GrFragmentProcessor>(
-                new GrSimpleTextureEffect(std::move(proxy), matrix, srcColorType, p));
+                new GrSimpleTextureEffect(std::move(proxy), matrix, alphaType, p));
     }
     GrSimpleTextureEffect(const GrSimpleTextureEffect& src);
     std::unique_ptr<GrFragmentProcessor> clone() const override;
@@ -50,11 +50,11 @@
     SkMatrix44 matrix;
 
 private:
-    GrSimpleTextureEffect(sk_sp<GrTextureProxy> image, SkMatrix44 matrix, GrColorType srcColorType,
+    GrSimpleTextureEffect(sk_sp<GrSurfaceProxy> image, SkMatrix44 matrix, SkAlphaType alphaType,
                           GrSamplerState samplerParams)
             : INHERITED(kGrSimpleTextureEffect_ClassID,
                         (OptimizationFlags)ModulateForSamplerOptFlags(
-                                srcColorType,
+                                alphaType,
                                 samplerParams.wrapModeX() ==
                                                 GrSamplerState::WrapMode::kClampToBorder ||
                                         samplerParams.wrapModeY() ==
diff --git a/src/gpu/geometry/GrQuad.h b/src/gpu/geometry/GrQuad.h
index 9131ff6..8a41235 100644
--- a/src/gpu/geometry/GrQuad.h
+++ b/src/gpu/geometry/GrQuad.h
@@ -24,7 +24,7 @@
     // certain types of matrices:
     enum class Type {
         // The 4 points remain an axis-aligned rectangle; their logical indices may not respect
-        // TL, BL, TR, BR ordering if the transform was a 90 degre rotation or mirror.
+        // TL, BL, TR, BR ordering if the transform was a 90 degree rotation or mirror.
         kAxisAligned,
         // The 4 points represent a rectangle subjected to a rotation, its corners are right angles.
         kRectilinear,
@@ -37,37 +37,12 @@
     };
     static const int kTypeCount = static_cast<int>(Type::kLast) + 1;
 
+    // This enforces W == 1 for non-perspective quads, but does not initialize X or Y.
     GrQuad() = default;
 
     explicit GrQuad(const SkRect& rect)
             : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
-            , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom}
-            , fW{1.f, 1.f, 1.f, 1.f}
-            , fType(Type::kAxisAligned) {}
-
-    GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, Type type)
-            : fType(type) {
-        SkASSERT(type != Type::kPerspective);
-        xs.store(fX);
-        ys.store(fY);
-        fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
-    }
-
-    GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys,
-           const skvx::Vec<4, float>& ws, Type type)
-            : fType(type) {
-        xs.store(fX);
-        ys.store(fY);
-        ws.store(fW);
-    }
-
-    // Copy 4 values from each of the arrays into the quad's components
-    GrQuad(const float xs[4], const float ys[4], const float ws[4], Type type)
-            : fType(type) {
-        memcpy(fX, xs, 4 * sizeof(float));
-        memcpy(fY, ys, 4 * sizeof(float));
-        memcpy(fW, ws, 4 * sizeof(float));
-    }
+            , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {}
 
     static GrQuad MakeFromRect(const SkRect&, const SkMatrix&);
 
@@ -135,8 +110,7 @@
     bool asRect(SkRect* rect) const;
 
     // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be
-    // taken to keep its quad type aligned with the geometric nature of the new coordinates. This is
-    // no different than using the constructors that accept a quad type.
+    // taken to keep its quad type aligned with the geometric nature of the new coordinates.
     const float* xs() const { return fX; }
     float* xs() { return fX; }
     const float* ys() const { return fY; }
@@ -144,16 +118,42 @@
     const float* ws() const { return fW; }
     float* ws() { return fW; }
 
-    void setQuadType(Type newType) { fType = newType; }
+    // Automatically ensures ws are 1 if new type is not perspective.
+    void setQuadType(Type newType) {
+        if (newType != Type::kPerspective && fType == Type::kPerspective) {
+            fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
+        }
+        SkASSERT(newType == Type::kPerspective ||
+                 (SkScalarNearlyEqual(fW[0], 1.f) && SkScalarNearlyEqual(fW[1], 1.f) &&
+                  SkScalarNearlyEqual(fW[2], 1.f) && SkScalarNearlyEqual(fW[3], 1.f)));
+
+        fType = newType;
+    }
 private:
     template<typename T>
     friend class GrQuadListBase; // for access to fX, fY, fW
 
+    GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, Type type)
+            : fType(type) {
+        SkASSERT(type != Type::kPerspective);
+        xs.store(fX);
+        ys.store(fY);
+    }
+
+    GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys,
+           const skvx::Vec<4, float>& ws, Type type)
+            : fW{} // Include fW in member initializer to avoid redundant default initializer
+            , fType(type) {
+        xs.store(fX);
+        ys.store(fY);
+        ws.store(fW);
+    }
+
     float fX[4];
     float fY[4];
-    float fW[4];
+    float fW[4] = {1.f, 1.f, 1.f, 1.f};
 
-    Type fType;
+    Type fType = Type::kAxisAligned;
 };
 
 #endif
diff --git a/src/gpu/geometry/GrQuadBuffer.h b/src/gpu/geometry/GrQuadBuffer.h
index dbdc92c..c7309d4 100644
--- a/src/gpu/geometry/GrQuadBuffer.h
+++ b/src/gpu/geometry/GrQuadBuffer.h
@@ -71,14 +71,16 @@
 
         const T& metadata() const { this->validate(); return *(fBuffer->metadata(fCurrentEntry)); }
 
-        const GrQuad& deviceQuad() const { this->validate(); return fDeviceQuad; }
+        // The returned pointer is mutable so that the object can be used for scratch calculations
+        // during op preparation. However, any changes are not persisted in the GrQuadBuffer and
+        // subsequent calls to next() will overwrite the state of the GrQuad.
+        GrQuad* deviceQuad() { this->validate(); return &fDeviceQuad; }
 
-        // If isLocalValid() returns false, this returns an empty quad (all 0s) so that localQuad()
-        // can be called without triggering any sanitizers, for convenience when some other state
-        // ensures that the quad will eventually not be used.
-        const GrQuad& localQuad() const {
+        // If isLocalValid() returns false, this returns nullptr. Otherwise, the returned pointer
+        // is mutable in the same manner as deviceQuad().
+        GrQuad* localQuad() {
             this->validate();
-            return fLocalQuad;
+            return this->isLocalValid() ? &fLocalQuad : nullptr;
         }
 
         bool isLocalValid() const {
@@ -250,18 +252,9 @@
         memcpy(quad->xs(), coords, k3DQuadFloats * sizeof(float));
         coords = coords + k3DQuadFloats;
     } else {
-        // Fill in X and Y of the quad, and set W to 1s if needed
+        // Fill in X and Y of the quad, the setQuadType() below will set Ws to 1 if needed
         memcpy(quad->xs(), coords, k2DQuadFloats * sizeof(float));
         coords = coords + k2DQuadFloats;
-
-        if (quad->quadType() == GrQuad::Type::kPerspective) {
-            // The output quad was previously perspective, so its ws are not 1s
-            static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
-            memcpy(quad->ws(), kNoPerspectiveWs, 4 * sizeof(float));
-        }
-        // Else the quad should already have 1s in w
-        SkASSERT(quad->w(0) == 1.f && quad->w(1) == 1.f &&
-                 quad->w(2) == 1.f && quad->w(3) == 1.f);
     }
 
     quad->setQuadType(type);
@@ -350,10 +343,8 @@
     coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fDeviceType), coords, &fDeviceQuad);
     if (h->fHasLocals) {
         coords = fBuffer->unpackQuad(static_cast<GrQuad::Type>(h->fLocalType), coords, &fLocalQuad);
-    } else {
-        static const GrQuad kEmptyLocal(SkRect::MakeEmpty());
-        fLocalQuad = kEmptyLocal;
-    }
+    } // else localQuad() will return a nullptr so no need to reset fLocalQuad
+
     // At this point, coords points to the start of the next entry
     fNextEntry = static_cast<const char*>(static_cast<const void*>(coords));
     SkASSERT((fNextEntry - fCurrentEntry) == fBuffer->entrySize(h));
diff --git a/src/gpu/geometry/GrQuadUtils.cpp b/src/gpu/geometry/GrQuadUtils.cpp
index 78edc3c..85a47c9 100644
--- a/src/gpu/geometry/GrQuadUtils.cpp
+++ b/src/gpu/geometry/GrQuadUtils.cpp
@@ -15,6 +15,44 @@
 using V4f = skvx::Vec<4, float>;
 using M4f = skvx::Vec<4, int32_t>;
 
+#define AI SK_ALWAYS_INLINE
+
+static constexpr float kTolerance = 1e-2f;
+
+// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
+// order.
+static AI V4f next_cw(const V4f& v) {
+    return skvx::shuffle<2, 0, 3, 1>(v);
+}
+
+static AI V4f next_ccw(const V4f& v) {
+    return skvx::shuffle<1, 3, 0, 2>(v);
+}
+
+// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
+// e3 may be null if only 2D edges need to be corrected for.
+static AI void correct_bad_edges(const M4f& bad, V4f* e1, V4f* e2, V4f* e3) {
+    if (any(bad)) {
+        // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
+        *e1 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e1), *e1);
+        *e2 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e2), *e2);
+        if (e3) {
+            *e3 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e3), *e3);
+        }
+    }
+}
+
+// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
+static AI void correct_bad_coords(const M4f& bad, V4f* c1, V4f* c2, V4f* c3) {
+    if (any(bad)) {
+        *c1 = if_then_else(bad, next_ccw(*c1), *c1);
+        *c2 = if_then_else(bad, next_ccw(*c2), *c2);
+        if (c3) {
+            *c3 = if_then_else(bad, next_ccw(*c3), *c3);
+        }
+    }
+}
+
 // Since the local quad may not be type kRect, this uses the opposites for each vertex when
 // interpolating, and calculates new ws in addition to new xs, ys.
 static void interpolate_local(float alpha, int v0, int v1, int v2, int v3,
@@ -186,7 +224,7 @@
 
 // Calculates barycentric coordinates for each point in (testX, testY) in the triangle formed by
 // (x0,y0) - (x1,y1) - (x2, y2) and stores them in u, v, w.
-static void barycentric_coords(float x0, float y0, float x1, float y1, float x2, float y2,
+static bool barycentric_coords(float x0, float y0, float x1, float y1, float x2, float y2,
                                const V4f& testX, const V4f& testY,
                                V4f* u, V4f* v, V4f* w) {
     // Modeled after SkPathOpsQuad::pointInTriangle() but uses float instead of double, is
@@ -195,18 +233,41 @@
     float v0y = y2 - y0;
     float v1x = x1 - x0;
     float v1y = y1 - y0;
-    V4f v2x = testX - x0;
-    V4f v2y = testY - y0;
 
     float dot00 = v0x * v0x + v0y * v0y;
     float dot01 = v0x * v1x + v0y * v1y;
-    V4f   dot02 = v0x * v2x + v0y * v2y;
     float dot11 = v1x * v1x + v1y * v1y;
-    V4f   dot12 = v1x * v2x + v1y * v2y;
-    float invDenom = sk_ieee_float_divide(1.f, dot00 * dot11 - dot01 * dot01);
+
+    // Not yet 1/d, first check d != 0 with a healthy tolerance (worst case is we end up not
+    // cropping something we could have, which is better than cropping something we shouldn't have).
+    // The tolerance is partly so large because these comparisons operate in device px^4 units,
+    // with plenty of subtractions thrown in. The SkPathOpsQuad code's use of doubles helped, and
+    // because it only needed to return "inside triangle", it could compare against [0, denom] and
+    // skip the normalization entirely.
+    float invDenom = dot00 * dot11 - dot01 * dot01;
+    static constexpr SkScalar kEmptyTriTolerance = SK_Scalar1 / (1 << 5);
+    if (SkScalarNearlyZero(invDenom, kEmptyTriTolerance)) {
+        // The triangle was degenerate/empty, which can cause the following UVW calculations to
+        // return (0,0,1) for every test point. This in turn makes the cropping code think that the
+        // empty triangle contains the crop rect and we turn the draw into a fullscreen clear, which
+        // is definitely the utter opposite of what we'd expect for an empty shape.
+        return false;
+    } else {
+        // Safe to divide
+        invDenom = sk_ieee_float_divide(1.f, invDenom);
+    }
+
+    V4f v2x = testX - x0;
+    V4f v2y = testY - y0;
+
+    V4f dot02 = v0x * v2x + v0y * v2y;
+    V4f dot12 = v1x * v2x + v1y * v2y;
+
     *u = (dot11 * dot02 - dot01 * dot12) * invDenom;
     *v = (dot00 * dot12 - dot01 * dot02) * invDenom;
     *w = 1.f - *u - *v;
+
+    return true;
 }
 
 static M4f inside_triangle(const V4f& u, const V4f& v, const V4f& w) {
@@ -304,11 +365,14 @@
     // Calculate barycentric coordinates for the 4 rect corners in the 2 triangles that the quad
     // is tessellated into when drawn.
     V4f u1, v1, w1;
-    barycentric_coords(devX[0], devY[0], devX[1], devY[1], devX[2], devY[2], clipX, clipY,
-                       &u1, &v1, &w1);
     V4f u2, v2, w2;
-    barycentric_coords(devX[1], devY[1], devX[3], devY[3], devX[2], devY[2], clipX, clipY,
-                       &u2, &v2, &w2);
+    if (!barycentric_coords(devX[0], devY[0], devX[1], devY[1], devX[2], devY[2], clipX, clipY,
+                            &u1, &v1, &w1) ||
+        !barycentric_coords(devX[1], devY[1], devX[3], devY[3], devX[2], devY[2], clipX, clipY,
+                            &u2, &v2, &w2)) {
+        // Bad triangles, skip cropping
+        return false;
+    }
 
     // clipDevRect is completely inside this quad if each corner is in at least one of two triangles
     M4f inTri1 = inside_triangle(u1, v1, w1);
@@ -343,4 +407,478 @@
     return false;
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// TessellationHelper implementation
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void TessellationHelper::reset(const GrQuad& deviceQuad, const GrQuad* localQuad) {
+    // Record basic state that isn't recorded on the Vertices struct itself
+    fDeviceType = deviceQuad.quadType();
+    fLocalType = localQuad ? localQuad->quadType() : GrQuad::Type::kAxisAligned;
+
+    // Reset metadata validity
+    fOutsetRequestValid = false;
+    fEdgeEquationsValid = false;
+
+    // Set vertices to match the device and local quad
+    fOriginal.fX = deviceQuad.x4f();
+    fOriginal.fY = deviceQuad.y4f();
+    fOriginal.fW = deviceQuad.w4f();
+
+    if (localQuad) {
+        fOriginal.fU = localQuad->x4f();
+        fOriginal.fV = localQuad->y4f();
+        fOriginal.fR = localQuad->w4f();
+        fOriginal.fUVRCount = fLocalType == GrQuad::Type::kPerspective ? 3 : 2;
+    } else {
+        fOriginal.fUVRCount = 0;
+    }
+
+    // Calculate all projected edge vector values for this quad.
+    if (fDeviceType == GrQuad::Type::kPerspective) {
+        V4f iw = 1.0 / fOriginal.fW;
+        fEdgeVectors.fX2D = fOriginal.fX * iw;
+        fEdgeVectors.fY2D = fOriginal.fY * iw;
+    } else {
+        fEdgeVectors.fX2D = fOriginal.fX;
+        fEdgeVectors.fY2D = fOriginal.fY;
+    }
+
+    fEdgeVectors.fDX = next_ccw(fEdgeVectors.fX2D) - fEdgeVectors.fX2D;
+    fEdgeVectors.fDY = next_ccw(fEdgeVectors.fY2D) - fEdgeVectors.fY2D;
+    fEdgeVectors.fInvLengths = rsqrt(mad(fEdgeVectors.fDX, fEdgeVectors.fDX,
+                                         fEdgeVectors.fDY * fEdgeVectors.fDY));
+
+    // Normalize edge vectors
+    fEdgeVectors.fDX *= fEdgeVectors.fInvLengths;
+    fEdgeVectors.fDY *= fEdgeVectors.fInvLengths;
+
+    // Calculate angles between vectors
+    if (fDeviceType <= GrQuad::Type::kRectilinear) {
+        fEdgeVectors.fCosTheta = 0.f;
+        fEdgeVectors.fInvSinTheta = 1.f;
+    } else {
+        fEdgeVectors.fCosTheta = mad(fEdgeVectors.fDX, next_cw(fEdgeVectors.fDX),
+                                     fEdgeVectors.fDY * next_cw(fEdgeVectors.fDY));
+        // NOTE: if cosTheta is close to 1, inset/outset math will avoid the fast paths that rely
+        // on thefInvSinTheta since it will approach infinity.
+        fEdgeVectors.fInvSinTheta = rsqrt(1.f - fEdgeVectors.fCosTheta * fEdgeVectors.fCosTheta);
+    }
+
+    fVerticesValid = true;
+}
+
+const TessellationHelper::EdgeEquations& TessellationHelper::getEdgeEquations() {
+    if (!fEdgeEquationsValid) {
+        V4f dx = fEdgeVectors.fDX;
+        V4f dy = fEdgeVectors.fDY;
+        // Correct for bad edges by copying adjacent edge information into the bad component
+        correct_bad_edges(fEdgeVectors.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
+
+        V4f c = mad(dx, fEdgeVectors.fY2D, -dy * fEdgeVectors.fX2D);
+        // Make sure normals point into the shape
+        V4f test = mad(dy, next_cw(fEdgeVectors.fX2D), mad(-dx, next_cw(fEdgeVectors.fY2D), c));
+        if (any(test < -kTolerance)) {
+            fEdgeEquations.fA = -dy;
+            fEdgeEquations.fB = dx;
+            fEdgeEquations.fC = -c;
+        } else {
+            fEdgeEquations.fA = dy;
+            fEdgeEquations.fB = -dx;
+            fEdgeEquations.fC = c;
+        }
+
+        fEdgeEquationsValid = true;
+    }
+    return fEdgeEquations;
+}
+
+const TessellationHelper::OutsetRequest& TessellationHelper::getOutsetRequest(
+        const skvx::Vec<4, float>& edgeDistances) {
+    // Much of the code assumes that we start from positive distances and apply it unmodified to
+    // create an outset; knowing that it's outset simplifies degeneracy checking.
+    SkASSERT(all(edgeDistances >= 0.f));
+
+    // Rebuild outset request if invalid or if the edge distances have changed.
+    if (!fOutsetRequestValid || any(edgeDistances != fOutsetRequest.fEdgeDistances)) {
+        // Based on the edge distances, determine if it's acceptable to use fInvSinTheta to
+        // calculate the inset or outset geometry.
+        if (fDeviceType <= GrQuad::Type::kRectilinear) {
+            // Since it's rectangular, the width (edge[1] or edge[2]) collapses if subtracting
+            // (dist[0] + dist[3]) makes the new width negative (minus for inset, outsetting will
+            // never be degenerate in this case). The same applies for height (edge[0] or edge[3])
+            // and (dist[1] + dist[2]).
+            fOutsetRequest.fOutsetDegenerate = false;
+            float widthChange = edgeDistances[0] + edgeDistances[3];
+            float heightChange = edgeDistances[1] + edgeDistances[2];
+            // (1/len > 1/(edge sum) implies len - edge sum < 0.
+            fOutsetRequest.fInsetDegenerate =
+                    (widthChange > 0.f  && fEdgeVectors.fInvLengths[1] > 1.f / widthChange) ||
+                    (heightChange > 0.f && fEdgeVectors.fInvLengths[0] > 1.f / heightChange);
+        } else if (any(fEdgeVectors.fInvLengths >= 1.f / kTolerance)) {
+            // Have an edge that is effectively length 0, so we're dealing with a triangle, which
+            // must always go through the degenerate code path.
+            fOutsetRequest.fOutsetDegenerate = true;
+            fOutsetRequest.fInsetDegenerate = true;
+        } else {
+            // If possible, the corners will move +/-edgeDistances * 1/sin(theta). The entire
+            // request is degenerate if 1/sin(theta) -> infinity (or cos(theta) -> 1).
+            if (any(abs(fEdgeVectors.fCosTheta) >= 0.9f)) {
+                fOutsetRequest.fOutsetDegenerate = true;
+                fOutsetRequest.fInsetDegenerate = true;
+            } else {
+                // With an edge-centric view, an edge's length changes by
+                // edgeDistance * cos(pi - theta) / sin(theta) for each of its corners (the second
+                // corner uses ccw theta value). An edge's length also changes when its adjacent
+                // edges move, in which case it's updated by edgeDistance / sin(theta)
+                // (or cos(theta) for the other edge).
+
+                // cos(pi - theta) = -cos(theta)
+                V4f halfTanTheta = -fEdgeVectors.fCosTheta * fEdgeVectors.fInvSinTheta;
+                V4f edgeAdjust = edgeDistances * (halfTanTheta + next_ccw(halfTanTheta)) +
+                                 next_ccw(edgeDistances) * next_ccw(fEdgeVectors.fInvSinTheta) +
+                                 next_cw(edgeDistances) * fEdgeVectors.fInvSinTheta;
+
+                // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make
+                // the edge lengths negative, then it's degenerate.
+                V4f threshold = 0.1f - (1.f / fEdgeVectors.fInvLengths);
+                fOutsetRequest.fOutsetDegenerate = any(edgeAdjust < threshold);
+                fOutsetRequest.fInsetDegenerate = any(edgeAdjust > -threshold);
+            }
+        }
+
+        fOutsetRequest.fEdgeDistances = edgeDistances;
+        fOutsetRequestValid = true;
+    }
+    return fOutsetRequest;
+}
+
+void TessellationHelper::Vertices::moveAlong(const EdgeVectors& edgeVectors,
+                                             const V4f& signedEdgeDistances) {
+    // This shouldn't be called if fInvSinTheta is close to infinity (cosTheta close to 1).
+    SkASSERT(all(abs(edgeVectors.fCosTheta) < 0.9f));
+
+    // When the projected device quad is not degenerate, the vertex corners can move
+    // cornerOutsetLen along their edge and their cw-rotated edge. The vertex's edge points
+    // inwards and the cw-rotated edge points outwards, hence the minus-sign.
+    // The edge distances are rotated compared to the corner outsets and (dx, dy), since if
+    // the edge is "on" both its corners need to be moved along their other edge vectors.
+    V4f signedOutsets = -edgeVectors.fInvSinTheta * next_cw(signedEdgeDistances);
+    V4f signedOutsetsCW = edgeVectors.fInvSinTheta * signedEdgeDistances;
+
+    // x = x + outset * mask * next_cw(xdiff) - outset * next_cw(mask) * xdiff
+    fX += mad(signedOutsetsCW, next_cw(edgeVectors.fDX), signedOutsets * edgeVectors.fDX);
+    fY += mad(signedOutsetsCW, next_cw(edgeVectors.fDY), signedOutsets * edgeVectors.fDY);
+    if (fUVRCount > 0) {
+        // We want to extend the texture coords by the same proportion as the positions.
+        signedOutsets *= edgeVectors.fInvLengths;
+        signedOutsetsCW *= next_cw(edgeVectors.fInvLengths);
+        V4f du = next_ccw(fU) - fU;
+        V4f dv = next_ccw(fV) - fV;
+        fU += mad(signedOutsetsCW, next_cw(du), signedOutsets * du);
+        fV += mad(signedOutsetsCW, next_cw(dv), signedOutsets * dv);
+        if (fUVRCount == 3) {
+            V4f dr = next_ccw(fR) - fR;
+            fR += mad(signedOutsetsCW, next_cw(dr), signedOutsets * dr);
+        }
+    }
+}
+
+void TessellationHelper::Vertices::moveTo(const V4f& x2d, const V4f& y2d, const M4f& mask) {
+    // Left to right, in device space, for each point
+    V4f e1x = skvx::shuffle<2, 3, 2, 3>(fX) - skvx::shuffle<0, 1, 0, 1>(fX);
+    V4f e1y = skvx::shuffle<2, 3, 2, 3>(fY) - skvx::shuffle<0, 1, 0, 1>(fY);
+    V4f e1w = skvx::shuffle<2, 3, 2, 3>(fW) - skvx::shuffle<0, 1, 0, 1>(fW);
+    correct_bad_edges(mad(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
+
+    // // Top to bottom, in device space, for each point
+    V4f e2x = skvx::shuffle<1, 1, 3, 3>(fX) - skvx::shuffle<0, 0, 2, 2>(fX);
+    V4f e2y = skvx::shuffle<1, 1, 3, 3>(fY) - skvx::shuffle<0, 0, 2, 2>(fY);
+    V4f e2w = skvx::shuffle<1, 1, 3, 3>(fW) - skvx::shuffle<0, 0, 2, 2>(fW);
+    correct_bad_edges(mad(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
+
+    // Can only move along e1 and e2 to reach the new 2D point, so we have
+    // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
+    // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
+    // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
+    // the cNx and cNy coefficients are:
+    V4f c1x = e1w * x2d - e1x;
+    V4f c1y = e1w * y2d - e1y;
+    V4f c2x = e2w * x2d - e2x;
+    V4f c2y = e2w * y2d - e2y;
+    V4f c3x = fW * x2d - fX;
+    V4f c3y = fW * y2d - fY;
+
+    // Solve for a and b
+    V4f a, b, denom;
+    if (all(mask)) {
+        // When every edge is outset/inset, each corner can use both edge vectors
+        denom = c1x * c2y - c2x * c1y;
+        a = (c2x * c3y - c3x * c2y) / denom;
+        b = (c3x * c1y - c1x * c3y) / denom;
+    } else {
+        // Force a or b to be 0 if that edge cannot be used due to non-AA
+        M4f aMask = skvx::shuffle<0, 0, 3, 3>(mask);
+        M4f bMask = skvx::shuffle<2, 1, 2, 1>(mask);
+
+        // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
+        // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
+        // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
+        // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
+        M4f useC1x = abs(c1x) > abs(c1y);
+        M4f useC2x = abs(c2x) > abs(c2y);
+
+        denom = if_then_else(aMask,
+                        if_then_else(bMask,
+                                c1x * c2y - c2x * c1y,            /* A & B   */
+                                if_then_else(useC1x, c1x, c1y)),  /* A & !B  */
+                        if_then_else(bMask,
+                                if_then_else(useC2x, c2x, c2y),   /* !A & B  */
+                                V4f(1.f)));                       /* !A & !B */
+
+        a = if_then_else(aMask,
+                    if_then_else(bMask,
+                            c2x * c3y - c3x * c2y,                /* A & B   */
+                            if_then_else(useC1x, -c3x, -c3y)),    /* A & !B  */
+                    V4f(0.f)) / denom;                            /* !A      */
+        b = if_then_else(bMask,
+                    if_then_else(aMask,
+                            c3x * c1y - c1x * c3y,                /* A & B   */
+                            if_then_else(useC2x, -c3x, -c3y)),    /* !A & B  */
+                    V4f(0.f)) / denom;                            /* !B      */
+    }
+
+    V4f newW = fW + a * e1w + b * e2w;
+    // If newW < 0, scale a and b such that the point reaches the infinity plane instead of crossing
+    // This breaks orthogonality of inset/outsets, but GPUs don't handle negative Ws well so this
+    // is far less visually disturbing (likely not noticeable since it's at extreme perspective).
+    // The alternative correction (multiply xyw by -1) has the disadvantage of changing how local
+    // coordinates would be interpolated.
+    static const float kMinW = 1e-6f;
+    if (any(newW < 0.f)) {
+        V4f scale = if_then_else(newW < kMinW, (kMinW - fW) / (newW - fW), V4f(1.f));
+        a *= scale;
+        b *= scale;
+    }
+
+    fX += a * e1x + b * e2x;
+    fY += a * e1y + b * e2y;
+    fW += a * e1w + b * e2w;
+    correct_bad_coords(abs(denom) < kTolerance, &fX, &fY, &fW);
+
+    if (fUVRCount > 0) {
+        // Calculate R here so it can be corrected with U and V in case it's needed later
+        V4f e1u = skvx::shuffle<2, 3, 2, 3>(fU) - skvx::shuffle<0, 1, 0, 1>(fU);
+        V4f e1v = skvx::shuffle<2, 3, 2, 3>(fV) - skvx::shuffle<0, 1, 0, 1>(fV);
+        V4f e1r = skvx::shuffle<2, 3, 2, 3>(fR) - skvx::shuffle<0, 1, 0, 1>(fR);
+        correct_bad_edges(mad(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
+
+        V4f e2u = skvx::shuffle<1, 1, 3, 3>(fU) - skvx::shuffle<0, 0, 2, 2>(fU);
+        V4f e2v = skvx::shuffle<1, 1, 3, 3>(fV) - skvx::shuffle<0, 0, 2, 2>(fV);
+        V4f e2r = skvx::shuffle<1, 1, 3, 3>(fR) - skvx::shuffle<0, 0, 2, 2>(fR);
+        correct_bad_edges(mad(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
+
+        fU += a * e1u + b * e2u;
+        fV += a * e1v + b * e2v;
+        if (fUVRCount == 3) {
+            fR += a * e1r + b * e2r;
+            correct_bad_coords(abs(denom) < kTolerance, &fU, &fV, &fR);
+        } else {
+            correct_bad_coords(abs(denom) < kTolerance, &fU, &fV, nullptr);
+        }
+    }
+}
+
+void TessellationHelper::Vertices::asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType,
+                                             GrQuad* localOut, GrQuad::Type localType) const {
+    SkASSERT(deviceOut);
+    SkASSERT(fUVRCount == 0 || localOut);
+
+    fX.store(deviceOut->xs());
+    fY.store(deviceOut->ys());
+    if (deviceType == GrQuad::Type::kPerspective) {
+        fW.store(deviceOut->ws());
+    }
+    deviceOut->setQuadType(deviceType); // This sets ws == 1 when device type != perspective
+
+    if (fUVRCount > 0) {
+        fU.store(localOut->xs());
+        fV.store(localOut->ys());
+        if (fUVRCount == 3) {
+            fR.store(localOut->ws());
+        }
+        localOut->setQuadType(localType);
+    }
+}
+
+V4f TessellationHelper::EdgeEquations::estimateCoverage(const V4f& x2d, const V4f& y2d) const {
+    // Calculate distance of the 4 inset points (px, py) to the 4 edges
+    V4f d0 = mad(fA[0], x2d, mad(fB[0], y2d, fC[0]));
+    V4f d1 = mad(fA[1], x2d, mad(fB[1], y2d, fC[1]));
+    V4f d2 = mad(fA[2], x2d, mad(fB[2], y2d, fC[2]));
+    V4f d3 = mad(fA[3], x2d, mad(fB[3], y2d, fC[3]));
+
+    // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal
+    // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis
+    // so its height is d1 + d2. Pin each of these dimensions to [0, 1] and approximate the coverage
+    // at each point as clamp(d0+d3, 0, 1) x clamp(d1+d2, 0, 1). For rectilinear quads this is an
+    // accurate calculation of its area clipped to an aligned pixel. For arbitrary quads it is not
+    // mathematically accurate but qualitatively provides a stable value proportional to the size of
+    // the shape.
+    V4f w = max(0.f, min(1.f, d0 + d3));
+    V4f h = max(0.f, min(1.f, d1 + d2));
+    return w * h;
+}
+
+int TessellationHelper::computeDegenerateQuad(const V4f& signedEdgeDistances, V4f* x2d, V4f* y2d) {
+    // Move the edge by the signed edge adjustment.
+    const EdgeEquations& edges = this->getEdgeEquations();
+    V4f oc = edges.fC + signedEdgeDistances;
+
+    // There are 6 points that we care about to determine the final shape of the polygon, which
+    // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
+    // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
+    V4f denom = edges.fA * next_cw(edges.fB) - edges.fB * next_cw(edges.fA);
+    V4f px = (edges.fB * next_cw(oc) - oc * next_cw(edges.fB)) / denom;
+    V4f py = (oc * next_cw(edges.fA) - edges.fA * next_cw(oc)) / denom;
+    correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr);
+
+    // Calculate the signed distances from these 4 corners to the other two edges that did not
+    // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
+    // p(3) to e0,e2
+    V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(edges.fA) +
+                 py * skvx::shuffle<3, 3, 0, 0>(edges.fB) +
+                 skvx::shuffle<3, 3, 0, 0>(oc);
+    V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(edges.fA) +
+                 py * skvx::shuffle<1, 2, 1, 2>(edges.fB) +
+                 skvx::shuffle<1, 2, 1, 2>(oc);
+
+    // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
+    // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
+    // and we need to use a central point to represent it. If all four points are only on the
+    // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
+    // Otherwise, use a triangle that replaces the bad points with the intersections of
+    // (e1, e2) or (e0, e3) as needed.
+    M4f d1v0 = dists1 < kTolerance;
+    M4f d2v0 = dists2 < kTolerance;
+    M4f d1And2 = d1v0 & d2v0;
+    M4f d1Or2 = d1v0 | d2v0;
+
+    if (!any(d1Or2)) {
+        // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
+        // and use full coverage
+        *x2d = px;
+        *y2d = py;
+        return 4;
+    } else if (any(d1And2)) {
+        // A point failed against two edges, so reduce the shape to a single point, which we take as
+        // the center of the original quad to ensure it is contained in the intended geometry. Since
+        // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
+        SkPoint center = {0.25f * ((*x2d)[0] + (*x2d)[1] + (*x2d)[2] + (*x2d)[3]),
+                          0.25f * ((*y2d)[0] + (*y2d)[1] + (*y2d)[2] + (*y2d)[3])};
+        *x2d = center.fX;
+        *y2d = center.fY;
+        return 1;
+    } else if (all(d1Or2)) {
+        // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
+        // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
+        if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
+            // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
+            *x2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
+            *y2d = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
+        } else {
+            // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
+            *x2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
+            *y2d = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
+        }
+        return 2;
+    } else {
+        // This turns into a triangle. Replace corners as needed with the intersections between
+        // (e0,e3) and (e1,e2), which must now be calculated
+        using V2f = skvx::Vec<2, float>;
+        V2f eDenom = skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(edges.fB) -
+                      skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(edges.fA);
+        V2f ex = (skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(oc) -
+                   skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fB)) / eDenom;
+        V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fA) -
+                   skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(oc)) / eDenom;
+
+        if (SkScalarAbs(eDenom[0]) > kTolerance) {
+            px = if_then_else(d1v0, V4f(ex[0]), px);
+            py = if_then_else(d1v0, V4f(ey[0]), py);
+        }
+        if (SkScalarAbs(eDenom[1]) > kTolerance) {
+            px = if_then_else(d2v0, V4f(ex[1]), px);
+            py = if_then_else(d2v0, V4f(ey[1]), py);
+        }
+
+        *x2d = px;
+        *y2d = py;
+        return 3;
+    }
+}
+
+int TessellationHelper::adjustVertices(const skvx::Vec<4, float>& edgeDistances, bool inset,
+                                       Vertices* vertices) {
+    SkASSERT(vertices);
+    SkASSERT(vertices->fUVRCount == 0 || vertices->fUVRCount == 2 || vertices->fUVRCount == 3);
+
+    const OutsetRequest& outsetRequest = this->getOutsetRequest(edgeDistances);
+    // Insets are more likely to become degenerate than outsets, so this allows us to compute the
+    // outer geometry with the fast path and the inner geometry with a slow path if possible.
+    bool degenerate = inset ? outsetRequest.fInsetDegenerate : outsetRequest.fOutsetDegenerate;
+    V4f signedEdgeDistances = outsetRequest.fEdgeDistances;
+    if (inset) {
+        signedEdgeDistances *= -1.f;
+    }
+
+    if (fDeviceType == GrQuad::Type::kPerspective || degenerate) {
+        Vertices projected = { fEdgeVectors.fX2D, fEdgeVectors.fY2D, /*w*/ 1.f, 0.f, 0.f, 0.f, 0};
+        int vertexCount;
+        if (degenerate) {
+            // Must use the slow path to handle numerical issues and self intersecting geometry
+            vertexCount = computeDegenerateQuad(signedEdgeDistances, &projected.fX, &projected.fY);
+        } else {
+            // Move the projected quad with the fast path, even though we will reconstruct the
+            // perspective corners afterwards.
+            projected.moveAlong(fEdgeVectors, signedEdgeDistances);
+            vertexCount = 4;
+        }
+        vertices->moveTo(projected.fX, projected.fY, signedEdgeDistances != 0.f);
+        return vertexCount;
+    } else {
+        // Quad is 2D and the inset/outset request does not cause the geometry to self intersect, so
+        // we can directly move the corners along the already calculated edge vectors.
+        vertices->moveAlong(fEdgeVectors, signedEdgeDistances);
+        return 4;
+    }
+}
+
+V4f TessellationHelper::inset(const skvx::Vec<4, float>& edgeDistances,
+                              GrQuad* deviceInset, GrQuad* localInset) {
+    SkASSERT(fVerticesValid);
+
+    Vertices inset = fOriginal;
+    int vertexCount = this->adjustVertices(edgeDistances, true, &inset);
+    inset.asGrQuads(deviceInset, fDeviceType, localInset, fLocalType);
+
+    if (vertexCount < 3) {
+        // The interior has less than a full pixel's area so estimate reduced coverage using
+        // the distance of the inset's projected corners to the original edges.
+        return this->getEdgeEquations().estimateCoverage(inset.fX / inset.fW,
+                                                         inset.fY / inset.fW);
+    } else {
+        return 1.f;
+    }
+}
+
+void TessellationHelper::outset(const skvx::Vec<4, float>& edgeDistances,
+                                GrQuad* deviceOutset, GrQuad* localOutset) {
+    SkASSERT(fVerticesValid);
+
+    Vertices outset = fOriginal;
+    this->adjustVertices(edgeDistances, false, &outset);
+    outset.asGrQuads(deviceOutset, fDeviceType, localOutset, fLocalType);
+}
+
 }; // namespace GrQuadUtils
diff --git a/src/gpu/geometry/GrQuadUtils.h b/src/gpu/geometry/GrQuadUtils.h
index 53b53d2..58f6237 100644
--- a/src/gpu/geometry/GrQuadUtils.h
+++ b/src/gpu/geometry/GrQuadUtils.h
@@ -8,10 +8,12 @@
 #ifndef GrQuadUtils_DEFINED
 #define GrQuadUtils_DEFINED
 
+#include "include/private/SkVx.h"
+#include "src/gpu/geometry/GrQuad.h"
+
 enum class GrQuadAAFlags;
 enum class GrAA : bool;
 enum class GrAAType : unsigned;
-class GrQuad;
 struct SkRect;
 
 namespace GrQuadUtils {
@@ -38,6 +40,138 @@
     bool CropToRect(const SkRect& cropRect, GrAA cropAA, GrQuadAAFlags* edgeFlags, GrQuad* quad,
                     GrQuad* local=nullptr);
 
+    class TessellationHelper {
+    public:
+        // Set the original device and (optional) local coordinates that are inset or outset
+        // by the requested edge distances. Use nullptr if there are no local coordinates to update.
+        void reset(const GrQuad& deviceQuad, const GrQuad* localQuad);
+
+        // Calculates a new quadrilateral with edges parallel to the original except that they
+        // have been moved inwards by edgeDistances (which should be positive). Distances are
+        // ordered L, B, T, R to match CCW tristrip ordering of GrQuad vertices. Edges that are
+        // not moved (i.e. distance == 0) will not be used in calculations and the corners will
+        // remain on that edge.
+        //
+        // The per-vertex coverage will be returned. When the inset geometry does not collapse to
+        // a point or line, this will be 1.0 for every vertex. When it does collapse, the per-vertex
+        // coverages represent estimated pixel coverage to simulate drawing the subpixel-sized
+        // original quad.
+        //
+        // Note: the edge distances are in device pixel units, so after rendering the new quad
+        // edge's shortest distance to the original quad's edge would be equal to provided edge dist
+        skvx::Vec<4, float> inset(const skvx::Vec<4, float>& edgeDistances,
+                                  GrQuad* deviceInset, GrQuad* localInset);
+
+        // Calculates a new quadrilateral that outsets the original edges by the given distances.
+        // Other than moving edges outwards, this function is equivalent to inset(). If the exact
+        // same edge distances are provided, certain internal computations can be reused across
+        // consecutive calls to inset() and outset() (in any order).
+        void outset(const skvx::Vec<4, float>& edgeDistances,
+                    GrQuad* deviceOutset, GrQuad* localOutset);
+
+    private:
+        struct EdgeVectors;
+        struct OutsetRequest;
+
+        struct Vertices {
+            // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
+            skvx::Vec<4, float> fX, fY, fW;
+            // U, V, and R coordinates representing local quad.
+            // Ignored depending on uvrCount (0, 1, 2).
+            skvx::Vec<4, float> fU, fV, fR;
+            int fUVRCount;
+
+            // Update the device and optional local coordinates by moving the corners along their
+            // edge vectors such that the new edges have moved 'signedEdgeDistances' from their
+            // original lines. This should only be called if the 'edgeVectors' fInvSinTheta data is
+            // numerically sound.
+            void moveAlong(const EdgeVectors& edgeVectors,
+                           const skvx::Vec<4, float>& signedEdgeDistances);
+
+            // Update the device coordinates by deriving (x,y,w) that project to (x2d, y2d), with
+            // optional local coordinates updated to match the new vertices. It is assumed that
+            // 'mask' was respected when determing (x2d, y2d), but it is used to ensure that only
+            // unmasked unprojected edge vectors are used when computing device and local coords.
+            void moveTo(const skvx::Vec<4, float>& x2d,
+                        const skvx::Vec<4, float>& y2d,
+                        const skvx::Vec<4, int32_t>& mask);
+
+            void asGrQuads(GrQuad* deviceOut, GrQuad::Type deviceType,
+                           GrQuad* localOut, GrQuad::Type localType) const;
+        };
+
+        // NOTE: This struct is named 'EdgeVectors' because it holds a lot of cached calculations
+        // pertaining to the edge vectors of the input quad, projected into 2D device coordinates.
+        // While they are not direction vectors, this struct represents a convenient storage space
+        // for the projected corners of the quad.
+        struct EdgeVectors {
+            // Projected corners (x/w and y/w); these are the 2D coordinates that determine the
+            // actual edge direction vectors, dx, dy, and invLengths
+            skvx::Vec<4, float> fX2D, fY2D;
+            // Normalized edge vectors of the device space quad, ordered L, B, T, R
+            // (i.e. next_ccw(x) - x).
+            skvx::Vec<4, float> fDX, fDY;
+            // Reciprocal of edge length of the device space quad, i.e. 1 / sqrt(dx*dx + dy*dy)
+            skvx::Vec<4, float> fInvLengths;
+            // Theta represents the angle formed by the two edges connected at each corner.
+            skvx::Vec<4, float> fCosTheta;
+            skvx::Vec<4, float> fInvSinTheta; // 1 / sin(theta)
+        };
+
+        struct EdgeEquations {
+            // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
+            skvx::Vec<4, float> fA, fB, fC;
+
+            skvx::Vec<4, float> estimateCoverage(const skvx::Vec<4, float>& x2d,
+                                                 const skvx::Vec<4, float>& y2d) const;
+        };
+
+        struct OutsetRequest {
+            // Positive edge distances to move each edge of the quad. These distances represent the
+            // shortest (perpendicular) distance between the original edge and the inset or outset
+            // edge. If the distance is 0, then the edge will not move.
+            skvx::Vec<4, float> fEdgeDistances;
+            // True if the new corners cannot be calculated by simply adding scaled edge vectors.
+            // The quad may be degenerate because of the original geometry (near colinear edges), or
+            // be because of the requested edge distances (collapse of inset, etc.)
+            bool fInsetDegenerate;
+            bool fOutsetDegenerate;
+        };
+
+        Vertices            fOriginal;
+        EdgeVectors         fEdgeVectors;
+        GrQuad::Type        fDeviceType;
+        GrQuad::Type        fLocalType;
+
+        // Lazily computed as needed; use accessor functions instead of direct access.
+        OutsetRequest       fOutsetRequest;
+        EdgeEquations       fEdgeEquations;
+
+        // Validity of Vertices/EdgeVectors (always true after first call to set()).
+        bool fVerticesValid = false;
+        // Validity of outset request (true after calling getOutsetRequest() until next set() call
+        // or next inset/outset() with different edge distances).
+        bool fOutsetRequestValid = false;
+        // Validity of edge equations (true after calling getEdgeEquations() until next set() call).
+        bool fEdgeEquationsValid = false;
+
+        // The requested edge distances must be positive so that they can be reused between inset
+        // and outset calls.
+        const OutsetRequest& getOutsetRequest(const skvx::Vec<4, float>& edgeDistances);
+        const EdgeEquations& getEdgeEquations();
+
+        // Outsets or insets 'x2d' and 'y2d' in place. To be used when the interior is very small,
+        // edges are near parallel, or edges are very short/zero-length. Returns number of effective
+        // vertices in the degenerate quad.
+        int computeDegenerateQuad(const skvx::Vec<4, float>& signedEdgeDistances,
+                                  skvx::Vec<4, float>* x2d, skvx::Vec<4, float>* y2d);
+        // Outsets or insets 'vertices' by the given perpendicular 'edgeDistances'. If 'inset' is
+        // true the distances move the edges inwards; if it is false, the distances move outwards.
+        // Returns number of effective vertices in the adjusted quad.
+        int adjustVertices(const skvx::Vec<4, float>& edgeDistances, bool inset,
+                           Vertices* vertices);
+    };
+
 }; // namespace GrQuadUtils
 
 #endif
diff --git a/src/gpu/geometry/GrShape.cpp b/src/gpu/geometry/GrShape.cpp
index 4fc3472..a510a19 100644
--- a/src/gpu/geometry/GrShape.cpp
+++ b/src/gpu/geometry/GrShape.cpp
@@ -201,7 +201,7 @@
     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
     SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
     SkASSERT(pointCnt && verbCnt);
-    *key++ = path.getFillType();
+    *key++ = (uint32_t)path.getFillType();
     *key++ = verbCnt;
     memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
     int verbKeySize = SkAlign4(verbCnt);
@@ -273,7 +273,7 @@
             case Type::kRRect:
                 fRRectData.fRRect.writeToMemory(key);
                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
-                *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
+                *key = (fRRectData.fDir == SkPathDirection::kCCW) ? (1 << 31) : 0;
                 *key |= fRRectData.fInverted ? (1 << 30) : 0;
                 *key++ |= fRRectData.fStart;
                 SkASSERT(fRRectData.fStart < 8);
@@ -297,7 +297,7 @@
                 *key++ = fPathData.fGenID;
                 // We could canonicalize the fill rule for paths that don't differentiate between
                 // even/odd or winding fill (e.g. convex).
-                *key++ = this->path().getFillType();
+                *key++ = (uint32_t)this->path().getFillType();
                 break;
             }
         }
@@ -507,7 +507,7 @@
 void GrShape::attemptToSimplifyPath() {
     SkRect rect;
     SkRRect rrect;
-    SkPath::Direction rrectDir;
+    SkPathDirection rrectDir;
     unsigned rrectStart;
     bool inverted = this->path().isInverseFillType();
     SkPoint pts[2];
diff --git a/src/gpu/geometry/GrShape.h b/src/gpu/geometry/GrShape.h
index 37f38f8..ea87986 100644
--- a/src/gpu/geometry/GrShape.h
+++ b/src/gpu/geometry/GrShape.h
@@ -61,7 +61,7 @@
         this->attemptToSimplifyRRect();
     }
 
-    GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
+    GrShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted,
             const GrStyle& style)
         : fStyle(style) {
         this->initType(Type::kRRect);
@@ -160,7 +160,7 @@
     }
 
     /** Returns the unstyled geometry as a rrect if possible. */
-    bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
+    bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const {
         if (Type::kRRect != fType) {
             return false;
         }
@@ -255,12 +255,12 @@
             return false;
         }
 
-        SkPath::Direction dirs[2];
+        SkPathDirection dirs[2];
         if (!SkPathPriv::IsNestedFillRects(this->path(), rects, dirs)) {
             return false;
         }
 
-        if (SkPath::kWinding_FillType == this->path().getFillType() && dirs[0] == dirs[1]) {
+        if (SkPathFillType::kWinding == this->path().getNewFillType() && dirs[0] == dirs[1]) {
             // The two rects need to be wound opposite to each other
             return false;
         }
@@ -550,15 +550,14 @@
     const SkPath* originalPathForListeners() const;
 
     // Defaults to use when there is no distinction between even/odd and winding fills.
-    static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
-    static constexpr SkPath::FillType kDefaultPathInverseFillType =
-            SkPath::kInverseEvenOdd_FillType;
+    static constexpr SkPathFillType kDefaultPathFillType = SkPathFillType::kEvenOdd;
+    static constexpr SkPathFillType kDefaultPathInverseFillType = SkPathFillType::kInverseEvenOdd;
 
-    static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
+    static constexpr SkPathDirection kDefaultRRectDir = SkPathDirection::kCW;
     static constexpr unsigned kDefaultRRectStart = 0;
 
     static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
-                                                SkPath::Direction* dir) {
+                                                SkPathDirection* dir) {
         *dir = kDefaultRRectDir;
         // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
         // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
@@ -575,11 +574,11 @@
             // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
             return 2 * 2;
         } else if (swapX) {
-            *dir = SkPath::kCCW_Direction;
+            *dir = SkPathDirection::kCCW;
             // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
             return 2 * 1;
         } else if (swapY) {
-            *dir = SkPath::kCCW_Direction;
+            *dir = SkPathDirection::kCCW;
             // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
             return 2 * 3;
         }
@@ -587,7 +586,7 @@
     }
 
     static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
-                                                 SkPath::Direction* dir) {
+                                                 SkPathDirection* dir) {
         // This comes from SkPath's interface. The default for adding a SkRRect to a path is
         // clockwise beginning at starting index 6.
         static constexpr unsigned kPathRRectStartIdx = 6;
@@ -602,7 +601,7 @@
     union {
         struct {
             SkRRect fRRect;
-            SkPath::Direction fDir;
+            SkPathDirection fDir;
             unsigned fStart;
             bool fInverted;
         } fRRectData;
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index fd7ff88..bcefe4e 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -8,6 +8,7 @@
 #include "include/gpu/GrContextOptions.h"
 #include "src/core/SkTSearch.h"
 #include "src/core/SkTSort.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrRenderTargetProxyPriv.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrSurfaceProxyPriv.h"
@@ -36,14 +37,18 @@
     fVertexArrayObjectSupport = false;
     fDebugSupport = false;
     fES2CompatibilitySupport = false;
+    fDrawInstancedSupport = false;
     fDrawIndirectSupport = false;
+    fDrawRangeElementsSupport = false;
     fMultiDrawIndirectSupport = false;
     fBaseInstanceSupport = false;
+    fUseNonVBOVertexAndIndexDynamicData = false;
     fIsCoreProfile = false;
     fBindFragDataLocationSupport = false;
     fRectangleTextureSupport = false;
     fRGBA8888PixelsOpsAreSlow = false;
     fPartialFBOReadIsSlow = false;
+    fBindUniformLocationSupport = false;
     fMipMapLevelAndLodControlSupport = false;
     fRGBAToBGRAReadbackConversionsAreSlow = false;
     fUseBufferDataNullHint = false;
@@ -692,8 +697,7 @@
     // already been detected.
     this->initFormatTable(ctxInfo, gli, formatWorkarounds);
 
-    this->applyOptionsOverrides(contextOptions);
-    shaderCaps->applyOptionsOverrides(contextOptions);
+    this->finishInitialization(contextOptions);
 
     // For now these two are equivalent but we could have dst read in shader via some other method.
     shaderCaps->fDstReadInShaderSupport = shaderCaps->fFBFetchSupport;
@@ -839,24 +843,15 @@
     }  // Not sure for WebGL
 
     if (GR_IS_GR_GL(standard)) {
-        shaderCaps->fSampleVariablesSupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration;
+        shaderCaps->fSampleMaskSupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration;
     } else if (GR_IS_GR_GL_ES(standard)) {
         if (ctxInfo.glslGeneration() >= k320es_GrGLSLGeneration) {
-            shaderCaps->fSampleVariablesSupport = true;
+            shaderCaps->fSampleMaskSupport = true;
         } else if (ctxInfo.hasExtension("GL_OES_sample_variables")) {
-            shaderCaps->fSampleVariablesSupport = true;
+            shaderCaps->fSampleMaskSupport = true;
             shaderCaps->fSampleVariablesExtensionString = "GL_OES_sample_variables";
         }
     }
-    shaderCaps->fSampleVariablesStencilSupport = shaderCaps->fSampleVariablesSupport;
-
-    if (kQualcomm_GrGLVendor == ctxInfo.vendor() || kATI_GrGLVendor == ctxInfo.vendor()) {
-        // FIXME: The sample mask round rect op draws nothing on several Adreno and Radeon bots.
-        // Other ops that use sample mask while rendering to stencil seem to work fine. Temporarily
-        // disable sample mask on color buffers while we investigate.
-        // http://skbug.com/8921
-        shaderCaps->fSampleVariablesSupport = false;
-    }
 
     shaderCaps->fVersionDeclString = get_glsl_version_decl_string(standard,
                                                                   shaderCaps->fGLSLGeneration,
@@ -951,13 +946,11 @@
     return true;
 }
 
-void GrGLCaps::initFSAASupport(const GrContextOptions& contextOptions, const GrGLContextInfo& ctxInfo,
-                               const GrGLInterface* gli) {
-    // We need dual source blending and the ability to disable multisample in order to support mixed
-    // samples in every corner case.
-    if (fMultisampleDisableSupport && this->shaderCaps()->dualSourceBlendingSupport()) {
-        fMixedSamplesSupport = ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") ||
-                               ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples");
+void GrGLCaps::initFSAASupport(const GrContextOptions& contextOptions,
+                               const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
+    if (ctxInfo.hasExtension("GL_NV_framebuffer_mixed_samples") ||
+        ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_mixed_samples")) {
+        fMixedSamplesSupport = true;
     }
 
     if (GR_IS_GR_GL(ctxInfo.standard())) {
@@ -1049,8 +1042,6 @@
         gS8    = {GR_GL_STENCIL_INDEX8,   8,                8,                false},
         gS16   = {GR_GL_STENCIL_INDEX16,  16,               16,               false},
         gD24S8 = {GR_GL_DEPTH24_STENCIL8, 8,                32,               true },
-        gS4    = {GR_GL_STENCIL_INDEX4,   4,                4,                false},
-    //  gS     = {GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false},
         gDS    = {GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true };
 
     if (GR_IS_GR_GL(ctxInfo.standard())) {
@@ -1066,9 +1057,6 @@
         fStencilFormats.push_back() = gS16;
         if (supportsPackedDS) {
             fStencilFormats.push_back() = gD24S8;
-        }
-        fStencilFormats.push_back() = gS4;
-        if (supportsPackedDS) {
             fStencilFormats.push_back() = gDS;
         }
     } else if (GR_IS_GR_GL_ES(ctxInfo.standard())) {
@@ -1077,14 +1065,10 @@
         // ES doesn't support using the unsized format.
 
         fStencilFormats.push_back() = gS8;
-        //fStencilFormats.push_back() = gS16;
         if (ctxInfo.version() >= GR_GL_VER(3,0) ||
             ctxInfo.hasExtension("GL_OES_packed_depth_stencil")) {
             fStencilFormats.push_back() = gD24S8;
         }
-        if (ctxInfo.hasExtension("GL_OES_stencil4")) {
-            fStencilFormats.push_back() = gS4;
-        }
     } else if (GR_IS_GR_WEBGL(ctxInfo.standard())) {
         fStencilFormats.push_back() = gS8;
         if (ctxInfo.version() >= GR_GL_VER(2,0)) {
@@ -1194,7 +1178,7 @@
         writer->appendHexU32("i_for_renderbuffer", fFormatTable[i].fInternalFormatForRenderbuffer);
         writer->appendHexU32("default_ex_format", fFormatTable[i].fDefaultExternalFormat);
         writer->appendHexU32("default_ex_type", fFormatTable[i].fDefaultExternalType);
-        writer->appendHexU64("bpp", fFormatTable[i].fBytesPerPixel);
+        writer->appendHexU32("default_color_type", (uint32_t)fFormatTable[i].fDefaultColorType);
 
         writer->beginArray("surface color types");
         for (int j = 0; j < fFormatTable[i].fColorTypeInfoCount; ++j) {
@@ -1234,12 +1218,14 @@
     *externalFormat = info.fDefaultExternalFormat;
 }
 
-void GrGLCaps::getTexSubImageZeroFormatTypeAndBpp(GrGLFormat format, GrGLenum* externalFormat,
-                                                  GrGLenum* externalType, size_t* bpp) const {
+void GrGLCaps::getTexSubImageDefaultFormatTypeAndColorType(GrGLFormat format,
+                                                           GrGLenum* externalFormat,
+                                                           GrGLenum* externalType,
+                                                           GrColorType* colorType) const {
     const auto& info = this->getFormatInfo(format);
     *externalType = info.fDefaultExternalType;
     *externalFormat = info.fDefaultExternalFormat;
-    *bpp = info.fBytesPerPixel;
+    *colorType = info.fDefaultColorType;
 }
 
 void GrGLCaps::getTexSubImageExternalFormatAndType(GrGLFormat surfaceFormat,
@@ -1343,6 +1329,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGBA8;
         info.fDefaultExternalFormat = GR_GL_RGBA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kRGBA_8888;
         info.fBytesPerPixel = 4;
         info.fFlags = FormatInfo::kTexturable_Flag;
         if (GR_IS_GR_GL(standard)) {
@@ -1460,6 +1447,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_R8;
         info.fDefaultExternalFormat = GR_GL_RED;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kR_8;
         info.fBytesPerPixel = 1;
         bool r8Support = false;
         if (GR_IS_GR_GL(standard)) {
@@ -1580,6 +1568,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_ALPHA8;
         info.fDefaultExternalFormat = GR_GL_ALPHA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kAlpha_8;
         info.fBytesPerPixel = 1;
         if (alpha8IsValidForGL || alpha8IsValidForGLES || alpha8IsValidForWebGL) {
             info.fFlags = FormatInfo::kTexturable_Flag;
@@ -1654,6 +1643,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_LUMINANCE8;
         info.fDefaultExternalFormat = GR_GL_LUMINANCE;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kGray_8;
         info.fBytesPerPixel = 1;
         bool lum8Supported = false;
         bool lum8SizedFormatSupported = false;
@@ -1755,6 +1745,7 @@
 
         info.fDefaultExternalFormat = GR_GL_BGRA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kBGRA_8888;
         info.fBytesPerPixel = 4;
 
         GrGLenum bgraTexImageFormat;
@@ -1859,6 +1850,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGB565;
         info.fDefaultExternalFormat = GR_GL_RGB;
         info.fDefaultExternalType = GR_GL_UNSIGNED_SHORT_5_6_5;
+        info.fDefaultColorType = GrColorType::kBGR_565;
         info.fBytesPerPixel = 2;
         if (GR_IS_GR_GL(standard)) {
             if (version >= GR_GL_VER(4, 2) || ctxInfo.hasExtension("GL_ARB_ES2_compatibility")) {
@@ -1928,6 +1920,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGBA16F;
         info.fDefaultExternalFormat = GR_GL_RGBA;
         info.fDefaultExternalType = halfFloatType;
+        info.fDefaultColorType = GrColorType::kRGBA_F16;
         info.fBytesPerPixel = 8;
         bool rgba16FTextureSupport = false;
         bool rgba16FRenderTargetSupport = false;
@@ -2062,6 +2055,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_R16F;
         info.fDefaultExternalFormat = GR_GL_RED;
         info.fDefaultExternalType = halfFloatType;
+        info.fDefaultColorType = GrColorType::kR_F16;
         info.fBytesPerPixel = 2;
         bool r16FTextureSupport = false;
         bool r16FRenderTargetSupport = false;
@@ -2173,6 +2167,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_LUMINANCE16F;
         info.fDefaultExternalFormat = GR_GL_LUMINANCE;
         info.fDefaultExternalType = halfFloatType;
+        info.fDefaultColorType = GrColorType::kGray_F16;
         info.fBytesPerPixel = 2;
 
         if (lum16FSupported) {
@@ -2236,6 +2231,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGB8;
         info.fDefaultExternalFormat = GR_GL_RGB;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kRGB_888;
         info.fBytesPerPixel = 4; // We assume the GPU stores this format 4 byte aligned
         info.fFlags = FormatInfo::kTexturable_Flag;
         if (GR_IS_GR_GL(standard)) {
@@ -2315,6 +2311,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RG8;
         info.fDefaultExternalFormat = GR_GL_RG;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kRG_88;
         info.fBytesPerPixel = 2;
         bool rg8Support = false;
         if (GR_IS_GR_GL(standard)) {
@@ -2382,6 +2379,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGB10_A2;
         info.fDefaultExternalFormat = GR_GL_RGBA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_INT_2_10_10_10_REV;
+        info.fDefaultColorType = GrColorType::kRGBA_1010102;
         info.fBytesPerPixel = 4;
         if (GR_IS_GR_GL(standard) ||
            (GR_IS_GR_GL_ES(standard) && version >= GR_GL_VER(3, 0))) {
@@ -2442,6 +2440,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGBA4;
         info.fDefaultExternalFormat = GR_GL_RGBA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
+        info.fDefaultColorType = GrColorType::kABGR_4444;
         info.fBytesPerPixel = 2;
         info.fFlags = FormatInfo::kTexturable_Flag;
         if (GR_IS_GR_GL(standard)) {
@@ -2502,6 +2501,7 @@
         info.fFormatType = FormatType::kNormalizedFixedPoint;
         info.fInternalFormatForRenderbuffer = GR_GL_SRGB8_ALPHA8;
         info.fDefaultExternalType = GR_GL_UNSIGNED_BYTE;
+        info.fDefaultColorType = GrColorType::kRGBA_8888_SRGB;
         info.fBytesPerPixel = 4;
 
         // We may modify the default external format below.
@@ -2645,6 +2645,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_R16;
         info.fDefaultExternalFormat = GR_GL_RED;
         info.fDefaultExternalType = GR_GL_UNSIGNED_SHORT;
+        info.fDefaultColorType = GrColorType::kR_16;
         info.fBytesPerPixel = 2;
         bool r16Supported = false;
         if (GR_IS_GR_GL(standard)) {
@@ -2713,6 +2714,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RG16;
         info.fDefaultExternalFormat = GR_GL_RG;
         info.fDefaultExternalType = GR_GL_UNSIGNED_SHORT;
+        info.fDefaultColorType = GrColorType::kRG_1616;
         info.fBytesPerPixel = 4;
         bool rg16Supported = false;
         if (GR_IS_GR_GL(standard)) {
@@ -2785,6 +2787,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RGBA16;
         info.fDefaultExternalFormat = GR_GL_RGBA;
         info.fDefaultExternalType = GR_GL_UNSIGNED_SHORT;
+        info.fDefaultColorType = GrColorType::kRGBA_16161616;
         info.fBytesPerPixel = 8;
         if (rgba16Support) {
             info.fFlags = FormatInfo::kTexturable_Flag | msaaRenderFlags;
@@ -2871,6 +2874,7 @@
         info.fInternalFormatForRenderbuffer = GR_GL_RG16F;
         info.fDefaultExternalFormat = GR_GL_RG;
         info.fDefaultExternalType = halfFloatType;
+        info.fDefaultColorType = GrColorType::kRG_F16;
         info.fBytesPerPixel = 4;
         if (rg16FTextureSupport) {
             info.fFlags |= FormatInfo::kTexturable_Flag;
@@ -3362,6 +3366,12 @@
         fPerformStencilClearsAsDraws = true;
     }
 
+    if (ctxInfo.vendor() == kQualcomm_GrGLVendor) {
+        // It appears that all the Adreno GPUs have less than optimal performance when
+        // drawing w/ large index buffers.
+        fAvoidLargeIndexBufferDraws = true;
+    }
+
     // This was reproduced on the following configurations:
     // - A Galaxy J5 (Adreno 306) running Android 6 with driver 140.0
     // - A Nexus 7 2013 (Adreno 320) running Android 5 with driver 104.0
@@ -3693,9 +3703,10 @@
 
 #ifdef SK_BUILD_FOR_MAC
     // On a MacBookPro 11.5 running MacOS 10.13 with a Radeon M370X the TransferPixelsFrom test
-    // fails when transfering out from a GL_RG8 texture using GL_RG/GL_UNSIGNED_BYTE.
+    // fails when transferring out from a GL_RG8 texture using GL_RG/GL_UNSIGNED_BYTE.
     formatWorkarounds->fDisallowDirectRG8ReadPixels =
-            ctxInfo.renderer() == kAMDRadeonR9M3xx_GrGLRenderer;
+            ctxInfo.renderer() == kAMDRadeonR9M3xx_GrGLRenderer ||
+            ctxInfo.renderer() == kAMDRadeonProVegaxx_GrGLRenderer;
 #endif
 
 #ifdef SK_BUILD_FOR_ANDROID
@@ -3721,6 +3732,13 @@
     if (kAdreno3xx_GrGLRenderer == ctxInfo.renderer()) {
         fTiledRenderingSupport = false;
     }
+
+    if (kQualcomm_GrGLVendor == ctxInfo.vendor() || kATI_GrGLVendor == ctxInfo.vendor()) {
+        // The sample mask round rect op draws nothing on several Adreno and Radeon bots. Other ops
+        // that use sample mask while rendering to stencil seem to work fine.
+        // http://skbug.com/8921
+        shaderCaps->fCanOnlyUseSampleMaskWithStencil = true;
+    }
 }
 
 void GrGLCaps::onApplyOptionsOverrides(const GrContextOptions& options) {
@@ -3735,6 +3753,7 @@
         SkASSERT(!fDetachStencilFromMSAABuffersBeforeReadPixels);
         SkASSERT(!fDontSetBaseOrMaxLevelForExternalTextures);
         SkASSERT(!fNeverDisableColorWrites);
+        SkASSERT(!fShaderCaps->fCanOnlyUseSampleMaskWithStencil);
     }
     if (options.fDoManualMipmapping) {
         fDoManualMipmapping = true;
@@ -4112,6 +4131,11 @@
         case GrColorType::kAlpha_8xxx:
         case GrColorType::kAlpha_F32xxx:
         case GrColorType::kGray_8xxx:
+        case GrColorType::kRGB_888:
+        case GrColorType::kR_8:
+        case GrColorType::kR_16:
+        case GrColorType::kR_F16:
+        case GrColorType::kGray_F16:
             break;
     }
 
@@ -4205,6 +4229,13 @@
     return GrSwizzle::RGBA();
 }
 
+GrProgramDesc GrGLCaps::makeDesc(const GrRenderTarget* rt, const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    SkDEBUGCODE(bool result =) GrProgramDesc::Build(&desc, rt, programInfo, *this);
+    SkASSERT(result == desc.isValid());
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrGLCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 99058a6..5c30c0a 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -171,11 +171,13 @@
                                              GrGLenum* externalType) const;
 
     /**
-     * Gets the external format, type, and bytes per pixel to use when uploading zeros via
-     * glTexSubImage...() to clear the texture at creation.
+     * Gets the external format, type, and bytes per pixel to use when uploading solid color data
+     * via glTexSubImage...() to clear the texture at creation.
      */
-    void getTexSubImageZeroFormatTypeAndBpp(GrGLFormat format, GrGLenum* externalFormat,
-                                            GrGLenum* externalType, size_t* bpp) const;
+    void getTexSubImageDefaultFormatTypeAndColorType(GrGLFormat format,
+                                                     GrGLenum* externalFormat,
+                                                     GrGLenum* externalType,
+                                                     GrColorType* colorType) const;
 
     void getReadPixelsFormat(GrGLFormat surfaceFormat, GrColorType surfaceColorType,
                              GrColorType memoryColorType, GrGLenum* externalFormat,
@@ -423,6 +425,8 @@
     GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
     GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     GrGLStandard standard() const { return fStandard; }
 
@@ -652,11 +656,13 @@
         GrGLenum fInternalFormatForRenderbuffer = 0;
 
         // Default values to use along with fInternalFormatForTexImageOrStorage for function
-        // glTexImage2D when not input providing data (passing nullptr). Not defined for compressed
-        // formats. Also used to upload zeros to initially clear a texture.
+        // glTexImage2D when not input providing data (passing nullptr) or when clearing it by
+        // uploading a block of solid color data. Not defined for compressed formats.
         GrGLenum fDefaultExternalFormat = 0;
         GrGLenum fDefaultExternalType = 0;
-
+        // When the above two values are used to initialize a texture by uploading cleared data to
+        // it the data should be of this color type.
+        GrColorType fDefaultColorType = GrColorType::kUnknown;
         // This value is only valid for regular formats. Compressed formats will be 0.
         GrGLenum fBytesPerPixel = 0;
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index d9f6a96..d3fa6e1 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -1276,9 +1276,9 @@
                            fResetTimestampForTextureParameters);
     if (levelClearMask) {
         GrGLenum externalFormat, externalType;
-        size_t bpp;
-        this->glCaps().getTexSubImageZeroFormatTypeAndBpp(texDesc.fFormat, &externalFormat,
-                                                          &externalType, &bpp);
+        GrColorType colorType;
+        this->glCaps().getTexSubImageDefaultFormatTypeAndColorType(texDesc.fFormat, &externalFormat,
+                                                                   &externalType, &colorType);
         if (this->glCaps().clearTextureSupport()) {
             for (int i = 0; i < mipLevelCount; ++i) {
                 if (levelClearMask & (1U << i)) {
@@ -1311,6 +1311,7 @@
                     // Levels only get smaller as we proceed. Once we create a zeros use it for all
                     // smaller levels that need clearing.
                     if (!zeros) {
+                        size_t bpp = GrColorTypeBytesPerPixel(colorType);
                         size_t size = levelWidth * levelHeight * bpp;
                         zeros.reset(new char[size]());
                     }
@@ -1659,12 +1660,9 @@
 #endif
 }
 
-bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget,
-                           const GrProgramInfo& programInfo,
-                           GrPrimitiveType primitiveType) {
+bool GrGLGpu::flushGLState(GrRenderTarget* renderTarget, const GrProgramInfo& programInfo) {
 
-    sk_sp<GrGLProgram> program(fProgramCache->refProgram(this, renderTarget, programInfo,
-                                                         primitiveType));
+    sk_sp<GrGLProgram> program(fProgramCache->refProgram(this, renderTarget, programInfo));
     if (!program) {
         GrCapsDebugf(this->caps(), "Failed to create program!\n");
         return false;
@@ -1681,7 +1679,6 @@
     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(renderTarget);
     GrStencilSettings stencil;
     if (programInfo.pipeline().isStencilEnabled()) {
-        // TODO: attach stencil and create settings during render target flush.
         SkASSERT(glRT->renderTargetPriv().getStencilAttachment());
         stencil.reset(*programInfo.pipeline().getUserStencil(),
                       programInfo.pipeline().hasStencilClip(),
@@ -2105,7 +2102,7 @@
         GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
         const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
         const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
-        const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+        const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     if (!fCachedOpsRenderPass) {
         fCachedOpsRenderPass.reset(new GrGLOpsRenderPass(this));
     }
@@ -2200,22 +2197,7 @@
 
     SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
 
-    GrPrimitiveType primitiveType = meshes[0].primitiveType();
-
-#ifdef SK_DEBUG
-    // kPoints should never be intermingled in with the other primitive types
-    for (int i = 1; i < meshCount; ++i) {
-        if (primitiveType == GrPrimitiveType::kPoints) {
-            SkASSERT(meshes[i].primitiveType() == GrPrimitiveType::kPoints);
-        } else {
-            SkASSERT(meshes[i].primitiveType() != GrPrimitiveType::kPoints);
-        }
-    }
-#endif
-
-    // Passing 'primitiveType' here is a bit misleading. In GL's case it works out, since
-    // GL only cares if it is kPoints or not.
-    if (!this->flushGLState(renderTarget, programInfo, primitiveType)) {
+    if (!this->flushGLState(renderTarget, programInfo)) {
         return;
     }
 
@@ -2223,6 +2205,8 @@
     bool hasDynamicPrimProcTextures = programInfo.hasDynamicPrimProcTextures();
 
     for (int m = 0; m < meshCount; ++m) {
+        SkASSERT(meshes[m].primitiveType() == programInfo.primitiveType());
+
         if (auto barrierType = programInfo.pipeline().xferBarrierType(renderTarget->asTexture(),
                                                                       *this->caps())) {
             this->xferBarrier(renderTarget, barrierType);
@@ -2460,12 +2444,14 @@
 
             fHWStencilTestEnabled = kYes_TriState;
         }
-        if (stencilSettings.isTwoSided()) {
-            set_gl_stencil(this->glInterface(), stencilSettings.front(origin), GR_GL_FRONT);
-            set_gl_stencil(this->glInterface(), stencilSettings.back(origin), GR_GL_BACK);
+        if (!stencilSettings.isTwoSided()) {
+            set_gl_stencil(this->glInterface(), stencilSettings.singleSidedFace(),
+                           GR_GL_FRONT_AND_BACK);
         } else {
-            set_gl_stencil(
-                    this->glInterface(), stencilSettings.frontAndBack(), GR_GL_FRONT_AND_BACK);
+            set_gl_stencil(this->glInterface(), stencilSettings.postOriginCWFace(origin),
+                           GR_GL_FRONT);
+            set_gl_stencil(this->glInterface(), stencilSettings.postOriginCCWFace(origin),
+                           GR_GL_BACK);
         }
         fHWStencilSettings = stencilSettings;
         fHWStencilOrigin = origin;
@@ -3596,22 +3582,14 @@
     SkUNREACHABLE;
 }
 
-GrBackendTexture GrGLGpu::onCreateBackendTexture(int w, int h,
+GrBackendTexture GrGLGpu::onCreateBackendTexture(SkISize dimensions,
                                                  const GrBackendFormat& format,
-                                                 GrMipMapped mipMapped,
                                                  GrRenderable renderable,
-                                                 const SkPixmap srcData[], int numMipLevels,
-                                                 const SkColor4f* color,
+                                                 const BackendTextureData* data,
+                                                 int numMipLevels,
                                                  GrProtected isProtected) {
     this->handleDirtyContext();
 
-    SkDEBUGCODE(const GrCaps* caps = this->caps();)
-
-    // GrGpu::createBackendTexture should've ensured these conditions
-    SkASSERT(w >= 1 && w <= caps->maxTextureSize() && h >= 1 && h <= caps->maxTextureSize());
-    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
-    SkASSERT(mipMapped == GrMipMapped::kNo || caps->mipMapSupport());
-
     GrGLFormat glFormat = format.asGLFormat();
     if (glFormat == GrGLFormat::kUnknown) {
         return GrBackendTexture();  // invalid
@@ -3620,90 +3598,80 @@
     // Compressed formats go through onCreateCompressedBackendTexture
     SkASSERT(!GrGLFormatIsCompressed(glFormat));
 
-    GrPixelConfig config = gl_format_to_pixel_config(glFormat);
-
-    if (config == kUnknown_GrPixelConfig) {
-        return GrBackendTexture();  // invalid
-    }
-
     GrGLTextureInfo info;
     GrGLTextureParameters::SamplerOverriddenState initialState;
 
-    SkTDArray<GrMipLevel> texels;
-    SkAutoMalloc pixelStorage;
-
-    int mipLevelCount = 1;
-    GrColorType textureAndDataColorType = GrColorType::kUnknown;
-    if (srcData) {
-        mipLevelCount = numMipLevels;
-        textureAndDataColorType = SkColorTypeToGrColorType(srcData[0].colorType());
-        texels.append(mipLevelCount);
-        for (int i = 0; i < mipLevelCount; ++i) {
-            texels[i] = { srcData[i].addr(), srcData[i].rowBytes() };
-        }
-    } else if (color) {
-        // We don't currently communicate the intended color type in this case so reverse engineer
-        // it from the config. Note this is lossy.
-        textureAndDataColorType = GrPixelConfigToColorType(config);
-
-        if (GrMipMapped::kYes == mipMapped) {
-            mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
-        }
-
-        texels.append(mipLevelCount);
-        SkTArray<size_t> individualMipOffsets(mipLevelCount);
-
-        size_t bytesPerPixel = this->glCaps().bytesPerPixel(glFormat);
-
-        size_t totalSize = GrComputeTightCombinedBufferSize(
-                bytesPerPixel, w, h, &individualMipOffsets, mipLevelCount);
-
-        char* tmpPixels = (char*)pixelStorage.reset(totalSize);
-
-        GrFillInData(textureAndDataColorType, w, h, individualMipOffsets, tmpPixels, *color);
-        for (int i = 0; i < mipLevelCount; ++i) {
-            size_t offset = individualMipOffsets[i];
-
-            int twoToTheMipLevel = 1 << i;
-            int currentWidth = SkTMax(1, w / twoToTheMipLevel);
-
-            texels[i] = {&(tmpPixels[offset]), currentWidth * bytesPerPixel};
-        }
-    }
-
     GrSurfaceDesc desc;
-    desc.fWidth = w;
-    desc.fHeight = h;
-    desc.fConfig = config;
+    desc.fWidth = dimensions.width();
+    desc.fHeight = dimensions.height();
+    desc.fConfig = gl_format_to_pixel_config(glFormat);
+    if (desc.fConfig == kUnknown_GrPixelConfig) {
+        return GrBackendTexture();  // invalid
+    }
 
     info.fTarget = GR_GL_TEXTURE_2D;
     info.fFormat = GrGLFormatToEnum(glFormat);
-    info.fID = this->createTexture2D({desc.fWidth, desc.fHeight}, glFormat, renderable,
-                                     &initialState, SkTMax(1, texels.count()));
+    info.fID = this->createTexture2D(dimensions, glFormat, renderable, &initialState, numMipLevels);
     if (!info.fID) {
-        return GrBackendTexture();  // invalid
-    }
-    if (!texels.empty()) {
-        // TODO: move the texturability check up to GrGpu::createBackendTexture and just assert here
-        if (!this->caps()->isFormatTexturableAndUploadable(textureAndDataColorType, format)) {
-            return GrBackendTexture();  // invalid
-        }
-        if (!this->uploadTexData(glFormat, textureAndDataColorType, desc.fWidth, desc.fHeight,
-                                 GR_GL_TEXTURE_2D, 0, 0, desc.fWidth, desc.fHeight,
-                                 textureAndDataColorType, texels.begin(), texels.count())) {
-            GL_CALL(DeleteTextures(1, &info.fID));
-            return GrBackendTexture();
-        }
+        return {};
     }
 
-    // unbind the texture from the texture unit to avoid asserts
-    GL_CALL(BindTexture(info.fTarget, 0));
+    if (data && data->type() == BackendTextureData::Type::kPixmaps) {
+        SkTDArray<GrMipLevel> texels;
+        GrColorType colorType = SkColorTypeToGrColorType(data->pixmap(0).colorType());
+        // Incorporate the color type into the config to make it "specific" if applicable.
+        desc.fConfig = this->caps()->getConfigFromBackendFormat(format, colorType);
+        SkASSERT(desc.fConfig != kUnknown_GrPixelConfig);
+        texels.append(numMipLevels);
+        for (int i = 0; i < numMipLevels; ++i) {
+            texels[i] = {data->pixmap(i).addr(), data->pixmap(i).rowBytes()};
+        }
+        if (!this->uploadTexData(glFormat, colorType, desc.fWidth, desc.fHeight, GR_GL_TEXTURE_2D,
+                                 0, 0, desc.fWidth, desc.fHeight, colorType, texels.begin(),
+                                 texels.count())) {
+            GL_CALL(DeleteTextures(1, &info.fID));
+            return {};
+        }
+    } else if (data && data->type() == BackendTextureData::Type::kColor) {
+        // TODO: Unify this with the clear texture code in onCreateTexture().
+        GrColorType colorType;
+        GrGLenum externalFormat, externalType;
+        this->glCaps().getTexSubImageDefaultFormatTypeAndColorType(glFormat, &externalFormat,
+                                                                   &externalType, &colorType);
+        if (colorType == GrColorType::kUnknown) {
+            GL_CALL(DeleteTextures(1, &info.fID));
+            return {};
+        }
+
+        // Make one tight image at the base size and reuse it for smaller levels.
+        GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, dimensions);
+        auto rb = ii.minRowBytes();
+        std::unique_ptr<char[]> pixelStorage(new char[rb * dimensions.height()]);
+        if (!GrClearImage(ii, pixelStorage.get(), rb, data->color())) {
+            GL_CALL(DeleteTextures(1, &info.fID));
+            return {};
+        }
+
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
+        SkISize levelDimensions = dimensions;
+        for (int i = 0; i < numMipLevels; ++i) {
+            GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D, i, 0, 0, levelDimensions.width(),
+                                  levelDimensions.height(), externalFormat, externalType,
+                                  pixelStorage.get()));
+            levelDimensions = {SkTMax(1, levelDimensions.width() /2),
+                               SkTMax(1, levelDimensions.height()/2)};
+        }
+    }
+    // Unbind this texture from the scratch texture unit.
+    this->bindTextureToScratchUnit(GR_GL_TEXTURE_2D, 0);
 
     auto parameters = sk_make_sp<GrGLTextureParameters>();
     parameters->set(&initialState, GrGLTextureParameters::NonsamplerState(),
                     fResetTimestampForTextureParameters);
 
-    return GrBackendTexture(w, h, mipMapped, info, std::move(parameters));
+    auto mipMapped = numMipLevels > 1 ? GrMipMapped::kYes : GrMipMapped::kNo;
+    return GrBackendTexture(dimensions.width(), dimensions.height(), mipMapped, info,
+                            std::move(parameters));
 }
 
 void GrGLGpu::deleteBackendTexture(const GrBackendTexture& tex) {
@@ -3882,7 +3850,7 @@
     return attribState;
 }
 
-void GrGLGpu::onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess access,
+bool GrGLGpu::onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess access,
                             const GrFlushInfo& info, const GrPrepareForExternalIORequests&) {
     // If we inserted semaphores during the flush, we need to call GLFlush.
     bool insertedSemaphore = info.fNumSemaphores > 0 && this->caps()->semaphoreSupport();
@@ -3916,6 +3884,7 @@
         // See if any previously inserted finish procs are good to go.
         this->checkFinishProcs();
     }
+    return true;
 }
 
 void GrGLGpu::submit(GrOpsRenderPass* renderPass) {
@@ -3947,28 +3916,29 @@
     this->deleteSync((GrGLsync)fence);
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore(bool isOwned) {
+std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore(bool isOwned) {
     SkASSERT(this->caps()->semaphoreSupport());
     return GrGLSemaphore::Make(this, isOwned);
 }
 
-sk_sp<GrSemaphore> GrGLGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                 GrResourceProvider::SemaphoreWrapType wrapType,
-                                                 GrWrapOwnership ownership) {
+std::unique_ptr<GrSemaphore> GrGLGpu::wrapBackendSemaphore(
+        const GrBackendSemaphore& semaphore,
+        GrResourceProvider::SemaphoreWrapType wrapType,
+        GrWrapOwnership ownership) {
     SkASSERT(this->caps()->semaphoreSupport());
     return GrGLSemaphore::MakeWrapped(this, semaphore.glSync(), ownership);
 }
 
-void GrGLGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
-    GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore.get());
+void GrGLGpu::insertSemaphore(GrSemaphore* semaphore) {
+    GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore);
 
     GrGLsync sync;
     GL_CALL_RET(sync, FenceSync(GR_GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
     glSem->setSync(sync);
 }
 
-void GrGLGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
-    GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore.get());
+void GrGLGpu::waitSemaphore(GrSemaphore* semaphore) {
+    GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore);
 
     GL_CALL(WaitSync(glSem->sync(), 0, GR_GL_TIMEOUT_IGNORED));
 }
@@ -3991,10 +3961,11 @@
     GL_CALL(InsertEventMarker(strlen(msg), msg));
 }
 
-sk_sp<GrSemaphore> GrGLGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
+std::unique_ptr<GrSemaphore> GrGLGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
     // Set up a semaphore to be signaled once the data is ready, and flush GL
-    sk_sp<GrSemaphore> semaphore = this->makeSemaphore(true);
-    this->insertSemaphore(semaphore);
+    std::unique_ptr<GrSemaphore> semaphore = this->makeSemaphore(true);
+    SkASSERT(semaphore);
+    this->insertSemaphore(semaphore.get());
     // We must call flush here to make sure the GrGLSync object gets created and sent to the gpu.
     GL_CALL(Flush());
 
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 3af1dd5..da5e575 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -15,6 +15,7 @@
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrMesh.h"
 #include "src/gpu/GrNativeRect.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrWindowRectsState.h"
 #include "src/gpu/GrXferProcessor.h"
 #include "src/gpu/gl/GrGLContext.h"
@@ -122,7 +123,7 @@
             GrRenderTarget*, GrSurfaceOrigin, const SkIRect&,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) override;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) override;
 
     void invalidateBoundRenderTarget() {
         fHWBoundRenderTargetUniqueID.makeInvalid();
@@ -155,16 +156,17 @@
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                            GrResourceProvider::SemaphoreWrapType wrapType,
-                                            GrWrapOwnership ownership) override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
-    void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(
+            const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType,
+            GrWrapOwnership ownership) override;
+    void insertSemaphore(GrSemaphore* semaphore) override;
+    void waitSemaphore(GrSemaphore* semaphore) override;
 
     void checkFinishProcs() override;
 
-    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
+    std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
     void deleteSync(GrGLsync) const;
 
@@ -177,10 +179,12 @@
     GrGLGpu(std::unique_ptr<GrGLContext>, GrContext*);
 
     // GrGpu overrides
-    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
-                                            GrMipMapped, GrRenderable,
-                                            const SkPixmap srcData[], int numMipLevels,
-                                            const SkColor4f* color, GrProtected) override;
+    GrBackendTexture onCreateBackendTexture(SkISize,
+                                            const GrBackendFormat&,
+                                            GrRenderable,
+                                            const BackendTextureData*,
+                                            int numMipLevels,
+                                            GrProtected) override;
 
     void onResetContext(uint32_t resetBits) override;
 
@@ -271,10 +275,7 @@
     void setTextureUnit(int unitIdx);
 
     // Flushes state from GrPipeline to GL. Returns false if the state couldn't be set.
-    // willDrawPoints must be true if point primitives will be rendered after setting the GL state.
-    // If DynamicStateArrays is not null then dynamicStateArraysLength is the number of dynamic
-    // state entries in each array.
-    bool flushGLState(GrRenderTarget*, const GrProgramInfo&, GrPrimitiveType);
+    bool flushGLState(GrRenderTarget*, const GrProgramInfo&);
 
     void flushProgram(sk_sp<GrGLProgram>);
 
@@ -291,7 +292,7 @@
 
     void flushBlendAndColorWrite(const GrXferProcessor::BlendInfo& blendInfo, const GrSwizzle&);
 
-    void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+    bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                        const GrFlushInfo&, const GrPrepareForExternalIORequests&) override;
 
     bool waitSync(GrGLsync, uint64_t timeout, bool flush);
@@ -312,7 +313,7 @@
 
         void abandon();
         void reset();
-        GrGLProgram* refProgram(GrGLGpu*, GrRenderTarget*, const GrProgramInfo&, GrPrimitiveType);
+        GrGLProgram* refProgram(GrGLGpu*, GrRenderTarget*, const GrProgramInfo&);
         bool precompileShader(const SkData& key, const SkData& data);
 
     private:
diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp
index 592605e..c5f9f2a 100644
--- a/src/gpu/gl/GrGLGpuProgramCache.cpp
+++ b/src/gpu/gl/GrGLGpuProgramCache.cpp
@@ -47,13 +47,11 @@
 
 GrGLProgram* GrGLGpu::ProgramCache::refProgram(GrGLGpu* gpu,
                                                GrRenderTarget* renderTarget,
-                                               const GrProgramInfo& programInfo,
-                                               GrPrimitiveType primitiveType) {
-    // TODO: can this be unified between GL, Vk and Mtl?
-    // Get GrGLProgramDesc
-    GrProgramDesc desc;
+                                               const GrProgramInfo& programInfo) {
+    const GrCaps& caps = *gpu->caps();
 
-    if (!GrProgramDesc::Build(&desc, renderTarget, programInfo, primitiveType, gpu)) {
+    GrProgramDesc desc = caps.makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(gpu->caps(), "Failed to gl program descriptor!\n");
         return nullptr;
     }
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
index e001bf9..c588e28 100644
--- a/src/gpu/gl/GrGLPath.cpp
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -193,15 +193,15 @@
 /*
  * For now paths only natively support winding and even odd fill types
  */
-static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) {
+static GrPathRendering::FillType convert_skpath_filltype(SkPathFillType fill) {
     switch (fill) {
         default:
             SK_ABORT("Incomplete Switch\n");
-        case SkPath::kWinding_FillType:
-        case SkPath::kInverseWinding_FillType:
+        case SkPathFillType::kWinding:
+        case SkPathFillType::kInverseWinding:
             return GrPathRendering::kWinding_FillType;
-        case SkPath::kEvenOdd_FillType:
-        case SkPath::kInverseEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
+        case SkPathFillType::kInverseEvenOdd:
             return GrPathRendering::kEvenOdd_FillType;
     }
 }
@@ -317,7 +317,7 @@
         fShouldFill = stroke.isFillStyle() ||
                 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style;
 
-        fFillType = convert_skpath_filltype(skPath->getFillType());
+        fFillType = convert_skpath_filltype(skPath->getNewFillType());
         fBounds = skPath->getBounds();
         SkScalar radius = stroke.getInflationRadius();
         fBounds.outset(radius, radius);
diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp
index 884bb9f..3d16543 100644
--- a/src/gpu/gl/GrGLPathRendering.cpp
+++ b/src/gpu/gl/GrGLPathRendering.cpp
@@ -99,9 +99,9 @@
 
     this->flushPathStencilSettings(*args.fStencil);
 
-    GrGLenum fillMode =
-        gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.frontAndBack().fPassOp);
-    GrGLint writeMask = fHWPathStencilSettings.frontAndBack().fWriteMask;
+    GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
+            fHWPathStencilSettings.singleSidedFace().fPassOp);
+    GrGLint writeMask = fHWPathStencilSettings.singleSidedFace().fWriteMask;
 
     if (glPath->shouldFill()) {
         GL_CALL(StencilFillPath(glPath->pathID(), fillMode, writeMask));
@@ -115,7 +115,7 @@
                                    const GrProgramInfo& programInfo,
                                    const GrStencilSettings& stencilPassSettings,
                                    const GrPath* path) {
-    if (!this->gpu()->flushGLState(renderTarget, programInfo, GrPrimitiveType::kPath)) {
+    if (!this->gpu()->flushGLState(renderTarget, programInfo)) {
         return;
     }
 
@@ -123,9 +123,9 @@
 
     this->flushPathStencilSettings(stencilPassSettings);
 
-    GrGLenum fillMode =
-        gr_stencil_op_to_gl_path_rendering_fill_mode(fHWPathStencilSettings.frontAndBack().fPassOp);
-    GrGLint writeMask = fHWPathStencilSettings.frontAndBack().fWriteMask;
+    GrGLenum fillMode = gr_stencil_op_to_gl_path_rendering_fill_mode(
+            fHWPathStencilSettings.singleSidedFace().fPassOp);
+    GrGLint writeMask = fHWPathStencilSettings.singleSidedFace().fWriteMask;
 
     if (glPath->shouldStroke()) {
         if (glPath->shouldFill()) {
@@ -247,14 +247,14 @@
         SkASSERT(stencilSettings.isValid());
         // Just the func, ref, and mask is set here. The op and write mask are params to the call
         // that draws the path to the SB (glStencilFillPath)
-        uint16_t ref = stencilSettings.frontAndBack().fRef;
-        GrStencilTest test = stencilSettings.frontAndBack().fTest;
-        uint16_t testMask = stencilSettings.frontAndBack().fTestMask;
+        uint16_t ref = stencilSettings.singleSidedFace().fRef;
+        GrStencilTest test = stencilSettings.singleSidedFace().fTest;
+        uint16_t testMask = stencilSettings.singleSidedFace().fTestMask;
 
         if (!fHWPathStencilSettings.isValid() ||
-            ref != fHWPathStencilSettings.frontAndBack().fRef ||
-            test != fHWPathStencilSettings.frontAndBack().fTest ||
-            testMask != fHWPathStencilSettings.frontAndBack().fTestMask) {
+            ref != fHWPathStencilSettings.singleSidedFace().fRef ||
+            test != fHWPathStencilSettings.singleSidedFace().fTest ||
+            testMask != fHWPathStencilSettings.singleSidedFace().fTestMask) {
             GL_CALL(PathStencilFunc(GrToGLStencilFunc(test), ref, testMask));
         }
         fHWPathStencilSettings = stencilSettings;
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 38f81ca..53760e6 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -84,8 +84,8 @@
     // We must bind to texture units in the same order in which we set the uniforms in
     // GrGLProgramDataManager. That is, we bind textures for processors in this order:
     // primProc, fragProcs, XP.
-    fPrimitiveProcessor->setData(fProgramDataManager, programInfo.primProc(),
-                                 GrFragmentProcessor::CoordTransformIter(programInfo.pipeline()));
+    GrFragmentProcessor::PipelineCoordTransformRange range(programInfo.pipeline());
+    fPrimitiveProcessor->setData(fProgramDataManager, programInfo.primProc(), range);
     if (programInfo.hasFixedPrimProcTextures()) {
         this->updatePrimitiveProcessorTextureBindings(programInfo.primProc(),
                                                       programInfo.fixedPrimProcTextures());
@@ -101,15 +101,16 @@
     fXferProcessor->setData(fProgramDataManager, xp, dstTexture, offset);
     if (dstTexture) {
         fGpu->bindTexture(nextTexSamplerIdx++, GrSamplerState::ClampNearest(),
-                          programInfo.pipeline().dstTextureProxy()->textureSwizzle(),
+                          programInfo.pipeline().dstProxyView().swizzle(),
                           static_cast<GrGLTexture*>(dstTexture));
     }
     SkASSERT(nextTexSamplerIdx == fNumTextureSamplers);
 }
 
 void GrGLProgram::updatePrimitiveProcessorTextureBindings(const GrPrimitiveProcessor& primProc,
-                                                          const GrTextureProxy* const proxies[]) {
+                                                          const GrSurfaceProxy* const proxies[]) {
     for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        SkASSERT(proxies[i]->asTextureProxy());
         auto* tex = static_cast<GrGLTexture*>(proxies[i]->peekTexture());
         fGpu->bindTexture(i, primProc.textureSampler(i).samplerState(),
                           primProc.textureSampler(i).swizzle(), tex);
@@ -117,21 +118,17 @@
 }
 
 void GrGLProgram::setFragmentData(const GrPipeline& pipeline, int* nextTexSamplerIdx) {
-    GrFragmentProcessor::Iter iter(pipeline);
+    GrFragmentProcessor::CIter fpIter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        glslFP->setData(fProgramDataManager, *fp);
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            const GrFragmentProcessor::TextureSampler& sampler = fp->textureSampler(i);
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        glslIter->setData(fProgramDataManager, *fpIter);
+        for (int i = 0; i < fpIter->numTextureSamplers(); ++i) {
+            const GrFragmentProcessor::TextureSampler& sampler = fpIter->textureSampler(i);
             fGpu->bindTexture((*nextTexSamplerIdx)++, sampler.samplerState(), sampler.swizzle(),
                               static_cast<GrGLTexture*>(sampler.peekTexture()));
         }
-        fp = iter.next();
-        glslFP = glslIter.next();
     }
-    SkASSERT(!fp && !glslFP);
+    SkASSERT(!fpIter && !glslIter);
 }
 
 void GrGLProgram::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin,
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index cf2e2aa..5554e7e 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -122,7 +122,7 @@
     void updateUniformsAndTextureBindings(const GrRenderTarget*, const GrProgramInfo&);
 
     void updatePrimitiveProcessorTextureBindings(const GrPrimitiveProcessor&,
-                                                 const GrTextureProxy* const[]);
+                                                 const GrSurfaceProxy* const[]);
 
     int vertexStride() const { return fVertexStride; }
     int instanceStride() const { return fInstanceStride; }
diff --git a/src/gpu/gl/GrGLSemaphore.cpp b/src/gpu/gl/GrGLSemaphore.cpp
index d3a224e..4ddfee8 100644
--- a/src/gpu/gl/GrGLSemaphore.cpp
+++ b/src/gpu/gl/GrGLSemaphore.cpp
@@ -10,20 +10,11 @@
 #include "src/gpu/gl/GrGLGpu.h"
 
 GrGLSemaphore::GrGLSemaphore(GrGLGpu* gpu, bool isOwned)
-        : INHERITED(gpu), fSync(0), fIsOwned(isOwned) {
-    isOwned ? this->registerWithCache(SkBudgeted::kNo)
-            : this->registerWithCacheWrapped(GrWrapCacheable::kNo);
+        : fGpu(gpu), fSync(0), fIsOwned(isOwned) {
 }
 
-void GrGLSemaphore::onRelease() {
+GrGLSemaphore::~GrGLSemaphore() {
     if (fSync && fIsOwned) {
-        static_cast<const GrGLGpu*>(this->getGpu())->deleteSync(fSync);
+        fGpu->deleteSync(fSync);
     }
-    fSync = 0;
-    INHERITED::onRelease();
-}
-
-void GrGLSemaphore::onAbandon() {
-    fSync = 0;
-    INHERITED::onAbandon();
 }
diff --git a/src/gpu/gl/GrGLSemaphore.h b/src/gpu/gl/GrGLSemaphore.h
index aff3b73..75880aa 100644
--- a/src/gpu/gl/GrGLSemaphore.h
+++ b/src/gpu/gl/GrGLSemaphore.h
@@ -16,19 +16,21 @@
 
 class GrGLSemaphore : public GrSemaphore {
 public:
-    static sk_sp<GrGLSemaphore> Make(GrGLGpu* gpu, bool isOwned) {
-        return sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu, isOwned));
+    static std::unique_ptr<GrGLSemaphore> Make(GrGLGpu* gpu, bool isOwned) {
+        return std::unique_ptr<GrGLSemaphore>(new GrGLSemaphore(gpu, isOwned));
     }
 
-    static sk_sp<GrGLSemaphore> MakeWrapped(GrGLGpu* gpu,
-                                            GrGLsync sync,
-                                            GrWrapOwnership ownership) {
-        auto sema = sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu,
-                                                           kBorrow_GrWrapOwnership != ownership));
+    static std::unique_ptr<GrGLSemaphore> MakeWrapped(GrGLGpu* gpu,
+                                                      GrGLsync sync,
+                                                      GrWrapOwnership ownership) {
+        auto sema = std::unique_ptr<GrGLSemaphore>(
+                new GrGLSemaphore(gpu, kBorrow_GrWrapOwnership != ownership));
         sema->setSync(sync);
         return sema;
     }
 
+    ~GrGLSemaphore() override;
+
     GrGLsync sync() const { return fSync; }
     void setSync(const GrGLsync& sync) { fSync = sync; }
 
@@ -41,9 +43,11 @@
 private:
     GrGLSemaphore(GrGLGpu* gpu, bool isOwned);
 
-    void onRelease() override;
-    void onAbandon() override;
+    void setIsOwned() override {
+        fIsOwned = true;
+    }
 
+    GrGLGpu* fGpu;
     GrGLsync fSync;
     bool     fIsOwned;
 
diff --git a/src/gpu/gl/GrGLUniformHandler.cpp b/src/gpu/gl/GrGLUniformHandler.cpp
index 56ed244..f888473 100644
--- a/src/gpu/gl/GrGLUniformHandler.cpp
+++ b/src/gpu/gl/GrGLUniformHandler.cpp
@@ -59,18 +59,19 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrGLUniformHandler::addSampler(const GrTextureProxy* texture,
+GrGLSLUniformHandler::SamplerHandle GrGLUniformHandler::addSampler(const GrSurfaceProxy* texture,
                                                                    const GrSamplerState&,
                                                                    const GrSwizzle& swizzle,
                                                                    const char* name,
                                                                    const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
+    SkASSERT(texture->asTextureProxy());
 
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrTextureType type = texture->textureType();
+    GrTextureType type = texture->backendFormat().textureType();
 
     UniformInfo& sampler = fSamplers.push_back();
     sampler.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
diff --git a/src/gpu/gl/GrGLUniformHandler.h b/src/gpu/gl/GrGLUniformHandler.h
index f577755..89549c7 100644
--- a/src/gpu/gl/GrGLUniformHandler.h
+++ b/src/gpu/gl/GrGLUniformHandler.h
@@ -42,7 +42,7 @@
         fUniforms[u.toIndex()].fVisibility |= visibility;
     }
 
-    SamplerHandle addSampler(const GrTextureProxy*, const GrSamplerState&, const GrSwizzle&,
+    SamplerHandle addSampler(const GrSurfaceProxy*, const GrSamplerState&, const GrSwizzle&,
                              const char* name, const GrShaderCaps*) override;
 
     const char* samplerVariable(SamplerHandle handle) const override {
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
index 219c5b2..ae5529b 100644
--- a/src/gpu/gl/GrGLUtil.cpp
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -121,7 +121,7 @@
     }
 
     static const char kChromium[] = "Chromium";
-    char suffix[SK_ARRAY_COUNT(kChromium)];
+    char suffix[SK_ARRAY_COUNT(kChromium)] = {0};
     if (0 == strcmp(rendererString, kChromium) ||
         (3 == sscanf(versionString, "OpenGL ES %d.%d %8s", &major, &minor, suffix) &&
          0 == strcmp(kChromium, suffix))) {
@@ -484,6 +484,13 @@
             if (3 == n) {
                 return kAMDRadeonHD7xxx_GrGLRenderer;
             }
+
+            int amdVegaModel=0;
+            n = sscanf(amdString, "Pro Vega %i", &amdVegaModel);
+            if (1 == n) {
+                return kAMDRadeonProVegaxx_GrGLRenderer;
+            }
+
         }
 
         if (strstr(rendererString, "llvmpipe")) {
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
index 9b72f0a..e5afc31 100644
--- a/src/gpu/gl/GrGLUtil.h
+++ b/src/gpu/gl/GrGLUtil.h
@@ -94,9 +94,10 @@
     kMaliT_GrGLRenderer,
     kANGLE_GrGLRenderer,
 
-    kAMDRadeonHD7xxx_GrGLRenderer,  // AMD Radeon HD 7000 Series
-    kAMDRadeonR9M3xx_GrGLRenderer,  // AMD Radeon R9 M300 Series
-    kAMDRadeonR9M4xx_GrGLRenderer,  // AMD Radeon R9 M400 Series
+    kAMDRadeonHD7xxx_GrGLRenderer,    // AMD Radeon HD 7000 Series
+    kAMDRadeonR9M3xx_GrGLRenderer,    // AMD Radeon R9 M300 Series
+    kAMDRadeonR9M4xx_GrGLRenderer,    // AMD Radeon R9 M400 Series
+    kAMDRadeonProVegaxx_GrGLRenderer, // AMD Radeon Pro Vega
 
     kOther_GrGLRenderer
 };
diff --git a/src/gpu/glsl/GrGLSLBlend.cpp b/src/gpu/glsl/GrGLSLBlend.cpp
index 2d1e93c..49810c3 100644
--- a/src/gpu/glsl/GrGLSLBlend.cpp
+++ b/src/gpu/glsl/GrGLSLBlend.cpp
@@ -5,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-#include "src/core/SkBlendModePriv.h"
 #include "src/gpu/glsl/GrGLSLBlend.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index e5e97de..8d6c94d 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -35,7 +35,7 @@
     }
     // if the fragment processor is invoked with overridden coordinates, it must *always* be invoked
     // with overridden coords
-    SkASSERT(args.fFp.computeLocalCoordsInVertexShader() == (skslCoords.length() == 0));
+    SkASSERT(args.fFp.coordTransformsApplyToLocalCoords() == (skslCoords.length() == 0));
     fragBuilder->codeAppendf("%s = %s(%s", outputColor, fFunctionNames[childIndex].c_str(),
                              inputColor ? inputColor : "half4(1)");
     if (skslCoords.length()) {
@@ -54,7 +54,7 @@
     while (childIndex >= (int) fFunctionNames.size()) {
         fFunctionNames.emplace_back();
     }
-    if (!args.fFp.computeLocalCoordsInVertexShader() && skslCoords.length() == 0) {
+    if (!args.fFp.coordTransformsApplyToLocalCoords() && skslCoords.length() == 0) {
         skslCoords = "_coords";
     }
     if (fFunctionNames[childIndex].size() == 0) {
@@ -117,14 +117,26 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-GrGLSLFragmentProcessor* GrGLSLFragmentProcessor::Iter::next() {
-    if (fFPStack.empty()) {
-        return nullptr;
+GrGLSLFragmentProcessor::Iter::Iter(std::unique_ptr<GrGLSLFragmentProcessor> fps[], int cnt) {
+    for (int i = cnt - 1; i >= 0; --i) {
+        fFPStack.push_back(fps[i].get());
     }
-    GrGLSLFragmentProcessor* back = fFPStack.back();
+}
+
+GrGLSLFragmentProcessor& GrGLSLFragmentProcessor::Iter::operator*() const {
+    return *fFPStack.back();
+}
+
+GrGLSLFragmentProcessor* GrGLSLFragmentProcessor::Iter::operator->() const {
+    return fFPStack.back();
+}
+
+GrGLSLFragmentProcessor::Iter& GrGLSLFragmentProcessor::Iter::operator++() {
+    SkASSERT(!fFPStack.empty());
+    const GrGLSLFragmentProcessor* back = fFPStack.back();
     fFPStack.pop_back();
     for (int i = back->numChildProcessors() - 1; i >= 0; --i) {
         fFPStack.push_back(back->childProcessor(i));
     }
-    return back;
+    return *this;
 }
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.h b/src/gpu/glsl/GrGLSLFragmentProcessor.h
index ad441d8..a1947d1 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.h
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.h
@@ -35,7 +35,7 @@
 
 private:
     /**
-     * This class allows the shader builder to provide each GrGLSLFragmentProcesor with an array of
+     * This class allows the shader builder to provide each GrGLSLFragmentProcessor with an array of
      * generated variables where each generated variable corresponds to an element of an array on
      * the GrFragmentProcessor that generated the GLSLFP. For example, this is used to provide a
      * variable holding transformed coords for each GrCoordTransform owned by the FP.
@@ -54,15 +54,15 @@
 
         BuilderInputProvider childInputs(int childIdx) const {
             const GrFragmentProcessor* child = &fFP->childProcessor(childIdx);
-            GrFragmentProcessor::Iter iter(fFP);
             int numToSkip = 0;
-            while (true) {
-                const GrFragmentProcessor* fp = iter.next();
-                if (fp == child) {
+            for (const auto& fp : GrFragmentProcessor::FPCRange(*fFP)) {
+                if (&fp == child) {
                     return BuilderInputProvider(child, fTs + numToSkip);
                 }
-                numToSkip += (fp->*COUNT)();
+                numToSkip += (fp.*COUNT)();
             }
+            SK_ABORT("Didn't find the child.");
+            return {nullptr, nullptr};
         }
 
     private:
@@ -134,9 +134,7 @@
 
     int numChildProcessors() const { return fChildProcessors.count(); }
 
-    GrGLSLFragmentProcessor* childProcessor(int index) {
-        return fChildProcessors[index];
-    }
+    GrGLSLFragmentProcessor* childProcessor(int index) const { return fChildProcessors[index]; }
 
     // Invoke the child with the default input color (solid white)
     inline void invokeChild(int childIndex, SkString* outputColor, EmitArgs& parentArgs,
@@ -168,17 +166,22 @@
 
     /**
      * Pre-order traversal of a GLSLFP hierarchy, or of multiple trees with roots in an array of
-     * GLSLFPS. This agrees with the traversal order of GrFragmentProcessor::Iter
+     * GLSLFPS. If initialized with an array color followed by coverage processors installed in a
+     * program thenthe iteration order will agree with a GrFragmentProcessor::Iter initialized with
+     * a GrPipeline that produces the same program key.
      */
-    class Iter : public SkNoncopyable {
+    class Iter {
     public:
-        explicit Iter(GrGLSLFragmentProcessor* fp) { fFPStack.push_back(fp); }
-        explicit Iter(std::unique_ptr<GrGLSLFragmentProcessor> fps[], int cnt) {
-            for (int i = cnt - 1; i >= 0; --i) {
-                fFPStack.push_back(fps[i].get());
-            }
-        }
-        GrGLSLFragmentProcessor* next();
+        Iter(std::unique_ptr<GrGLSLFragmentProcessor> fps[], int cnt);
+
+        GrGLSLFragmentProcessor& operator*() const;
+        GrGLSLFragmentProcessor* operator->() const;
+        Iter& operator++();
+        operator bool() const { return !fFPStack.empty(); }
+
+        // Because each iterator carries a stack we want to avoid copies.
+        Iter(const Iter&) = delete;
+        Iter& operator=(const Iter&) = delete;
 
     private:
         SkSTArray<4, GrGLSLFragmentProcessor*, true> fFPStack;
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index 85dc827..5976330 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -72,6 +72,9 @@
 }
 
 SkString GrGLSLFragmentShaderBuilder::ensureCoords2D(const GrShaderVar& coords) {
+    if (!coords.getName().size()) {
+        return SkString("_coords");
+    }
     if (kFloat3_GrSLType != coords.getType() && kHalf3_GrSLType != coords.getType()) {
         SkASSERT(kFloat2_GrSLType == coords.getType() || kHalf2_GrSLType == coords.getType());
         return coords.getName();
@@ -94,7 +97,7 @@
 void GrGLSLFragmentShaderBuilder::maskOffMultisampleCoverage(
         const char* mask, ScopeFlags scopeFlags) {
     const GrShaderCaps& shaderCaps = *fProgramBuilder->shaderCaps();
-    if (!shaderCaps.sampleVariablesSupport() && !shaderCaps.sampleVariablesStencilSupport()) {
+    if (!shaderCaps.sampleMaskSupport()) {
         SkDEBUGFAIL("Attempted to mask sample coverage without support.");
         return;
     }
@@ -105,15 +108,15 @@
     if (!fHasModifiedSampleMask) {
         fHasModifiedSampleMask = true;
         if (ScopeFlags::kTopLevel != scopeFlags) {
-            this->codePrependf("gl_SampleMask[0] = ~0;");
+            this->codePrependf("sk_SampleMask[0] = ~0;");
         }
         if (!(ScopeFlags::kInsideLoop & scopeFlags)) {
-            this->codeAppendf("gl_SampleMask[0] = (%s);", mask);
+            this->codeAppendf("sk_SampleMask[0] = (%s);", mask);
             return;
         }
     }
 
-    this->codeAppendf("gl_SampleMask[0] &= (%s);", mask);
+    this->codeAppendf("sk_SampleMask[0] &= (%s);", mask);
 }
 
 void GrGLSLFragmentShaderBuilder::applyFnToMultisampleMask(
@@ -160,14 +163,14 @@
                                                          GrGLSLFragmentProcessor::EmitArgs& args) {
     this->onBeforeChildProcEmitCode();
     this->nextStage();
-    if (!args.fFp.computeLocalCoordsInVertexShader() && args.fTransformedCoords.count() > 0) {
+    if (!args.fFp.coordTransformsApplyToLocalCoords() && args.fTransformedCoords.count() > 0) {
         // we currently only support overriding a single coordinate pair
         SkASSERT(args.fTransformedCoords.count() == 1);
         const GrGLSLProgramDataManager::UniformHandle& mat =
                                                           args.fTransformedCoords[0].fUniformMatrix;
         if (mat.isValid()) {
             args.fUniformHandler->updateUniformVisibility(mat, kFragment_GrShaderFlag);
-            this->codeAppendf("_coords = (float3(_coords, 1) * %s).xy;\n",
+            this->codeAppendf("_coords = (%s * float3(_coords, 1)).xy;\n",
                               args.fTransformedCoords[0].fMatrixCode.c_str());
         }
     }
@@ -179,7 +182,7 @@
     SkString result;
     this->emitFunction(kHalf4_GrSLType,
                        "stage",
-                       args.fFp.computeLocalCoordsInVertexShader() ? 1 : 2,
+                       args.fFp.coordTransformsApplyToLocalCoords() ? 1 : 2,
                        params,
                        this->code().c_str(),
                        &result);
@@ -291,7 +294,8 @@
 
     if (CustomFeatures::kSampleLocations & fProgramBuilder->processorFeatures()) {
         const SkTArray<SkPoint>& sampleLocations = fProgramBuilder->getSampleLocations();
-        this->definitions().append("const float2 _sampleOffsets[] = float2[](");
+        this->definitions().appendf("const float2 _sampleOffsets[%i] = float2[%i](",
+                                    sampleLocations.count(), sampleLocations.count());
         for (int i = 0; i < sampleLocations.count(); ++i) {
             SkPoint offset = sampleLocations[i] - SkPoint::Make(.5f, .5f);
             if (kBottomLeft_GrSurfaceOrigin == this->getSurfaceOrigin()) {
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
index d99239e..26288b9 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.cpp
@@ -67,48 +67,62 @@
     } else {
         localCoords.printf("float3(%s, 1)", localCoordsVar.c_str());
     }
-    int i = 0;
-    while (const GrCoordTransform* coordTransform = handler->nextCoordTransform()) {
-        SkString strUniName;
-        strUniName.printf("CoordTransformMatrix_%d", i);
-        const char* uniName;
-        fInstalledTransforms.push_back().fHandle = uniformHandler->addUniform(kVertex_GrShaderFlag,
-                                                                              kFloat3x3_GrSLType,
-                                                                              strUniName.c_str(),
-                                                                              &uniName).toIndex();
-        GrSLType varyingType = kFloat2_GrSLType;
-        if (localMatrix.hasPerspective() || coordTransform->getMatrix().hasPerspective()
-            || threeComponentLocalCoords) {
-            varyingType = kFloat3_GrSLType;
-        }
-        SkString strVaryingName;
-        strVaryingName.printf("TransformedCoords_%d", i);
-        GrGLSLVarying v(varyingType);
-        if (coordTransform->computeInVertexShader()) {
-            varyingHandler->addVarying(strVaryingName.c_str(), &v);
-
-            if (kFloat2_GrSLType == varyingType) {
-                vb->codeAppendf("%s = (%s * %s).xy;", v.vsOut(), uniName, localCoords.c_str());
-            } else {
-                vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, localCoords.c_str());
+    for (int i = 0; *handler; ++*handler, ++i) {
+        auto [coordTransform, fp] = handler->get();
+        if (coordTransform.isNoOp() && !fp.coordTransformsApplyToLocalCoords()) {
+            handler->omitCoordsForCurrCoordTransform();
+            fInstalledTransforms.push_back();
+        } else {
+            SkString strUniName;
+            strUniName.printf("CoordTransformMatrix_%d", i);
+            const char* uniName;
+            fInstalledTransforms.push_back().fHandle = uniformHandler
+                                                               ->addUniform(kVertex_GrShaderFlag,
+                                                                            kFloat3x3_GrSLType,
+                                                                            strUniName.c_str(),
+                                                                            &uniName)
+                                                               .toIndex();
+            GrSLType varyingType = kFloat2_GrSLType;
+            if (localMatrix.hasPerspective() || coordTransform.matrix().hasPerspective() ||
+                threeComponentLocalCoords) {
+                varyingType = kFloat3_GrSLType;
             }
+            SkString strVaryingName;
+            strVaryingName.printf("TransformedCoords_%d", i);
+            GrGLSLVarying v(varyingType);
+            if (fp.coordTransformsApplyToLocalCoords()) {
+                varyingHandler->addVarying(strVaryingName.c_str(), &v);
+
+                if (kFloat2_GrSLType == varyingType) {
+                    vb->codeAppendf("%s = (%s * %s).xy;", v.vsOut(), uniName, localCoords.c_str());
+                } else {
+                    vb->codeAppendf("%s = %s * %s;", v.vsOut(), uniName, localCoords.c_str());
+                }
+            }
+            handler->specifyCoordsForCurrCoordTransform(
+                    SkString(uniName),
+                    fInstalledTransforms.back().fHandle,
+                    GrShaderVar(SkString(v.fsIn()), varyingType));
         }
-        handler->specifyCoordsForCurrCoordTransform(SkString(uniName),
-                                                    fInstalledTransforms.back().fHandle,
-                                                    GrShaderVar(SkString(v.fsIn()), varyingType));
-        ++i;
     }
 }
 
 void GrGLSLGeometryProcessor::setTransformDataHelper(const SkMatrix& localMatrix,
                                                      const GrGLSLProgramDataManager& pdman,
-                                                     FPCoordTransformIter* transformIter) {
+                                                     const CoordTransformRange& transformRange) {
     int i = 0;
-    while (const GrCoordTransform* coordTransform = transformIter->next()) {
-        const SkMatrix& m = GetTransformMatrix(localMatrix, *coordTransform);
-        if (!fInstalledTransforms[i].fCurrentValue.cheapEqualTo(m)) {
-            pdman.setSkMatrix(fInstalledTransforms[i].fHandle.toIndex(), m);
-            fInstalledTransforms[i].fCurrentValue = m;
+    for (auto [transform, fp] : transformRange) {
+        if (fInstalledTransforms[i].fHandle.isValid()) {
+            SkMatrix m;
+            if (fp.coordTransformsApplyToLocalCoords()) {
+                m = GetTransformMatrix(transform, localMatrix);
+            } else {
+                m = GetTransformMatrix(transform, SkMatrix::I());
+            }
+            if (!fInstalledTransforms[i].fCurrentValue.cheapEqualTo(m)) {
+                pdman.setSkMatrix(fInstalledTransforms[i].fHandle.toIndex(), m);
+                fInstalledTransforms[i].fCurrentValue = m;
+            }
         }
         ++i;
     }
diff --git a/src/gpu/glsl/GrGLSLGeometryProcessor.h b/src/gpu/glsl/GrGLSLGeometryProcessor.h
index eb34412..c8f7895 100644
--- a/src/gpu/glsl/GrGLSLGeometryProcessor.h
+++ b/src/gpu/glsl/GrGLSLGeometryProcessor.h
@@ -26,7 +26,7 @@
     // A helper which subclasses can use to upload coord transform matrices in setData().
     void setTransformDataHelper(const SkMatrix& localMatrix,
                                 const GrGLSLProgramDataManager& pdman,
-                                FPCoordTransformIter*);
+                                const CoordTransformRange&);
 
     // Emit transformed local coords from the vertex shader as a uniform matrix and varying per
     // coord-transform. localCoordsVar must be a 2- or 3-component vector. If it is 3 then it is
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
index 9d35c44..c28b370 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp
@@ -14,10 +14,10 @@
 #include "src/gpu/glsl/GrGLSLUniformHandler.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
-SkMatrix GrGLSLPrimitiveProcessor::GetTransformMatrix(const SkMatrix& localMatrix,
-                                                      const GrCoordTransform& coordTransform) {
+SkMatrix GrGLSLPrimitiveProcessor::GetTransformMatrix(const GrCoordTransform& coordTransform,
+                                                      const SkMatrix& preMatrix) {
     SkMatrix combined;
-    combined.setConcat(coordTransform.getMatrix(), localMatrix);
+    combined.setConcat(coordTransform.matrix(), preMatrix);
     if (coordTransform.normalize()) {
         combined.postIDiv(coordTransform.peekTexture()->width(),
                           coordTransform.peekTexture()->height());
@@ -66,13 +66,19 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-const GrCoordTransform* GrGLSLPrimitiveProcessor::FPCoordTransformHandler::nextCoordTransform() {
-#ifdef SK_DEBUG
-    SkASSERT(nullptr == fCurr || fAddedCoord);
-    fAddedCoord = false;
-    fCurr = fIter.next();
-    return fCurr;
-#else
-    return fIter.next();
-#endif
+GrGLSLPrimitiveProcessor::FPCoordTransformHandler::FPCoordTransformHandler(
+        const GrPipeline& pipeline, SkTArray<TransformVar>* transformedCoordVars)
+        : fIter(pipeline), fTransformedCoordVars(transformedCoordVars) {}
+
+std::pair<const GrCoordTransform&, const GrFragmentProcessor&>
+GrGLSLPrimitiveProcessor::FPCoordTransformHandler::get() const {
+    return *fIter;
+}
+
+GrGLSLPrimitiveProcessor::FPCoordTransformHandler&
+GrGLSLPrimitiveProcessor::FPCoordTransformHandler::operator++() {
+    SkASSERT(fAddedCoord);
+    ++fIter;
+    SkDEBUGCODE(fAddedCoord = false;)
+    return *this;
 }
diff --git a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
index 12fb74f..f4ca1f3 100644
--- a/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
+++ b/src/gpu/glsl/GrGLSLPrimitiveProcessor.h
@@ -23,9 +23,9 @@
 
 class GrGLSLPrimitiveProcessor {
 public:
-    using UniformHandle        = GrGLSLProgramDataManager::UniformHandle;
-    using SamplerHandle        = GrGLSLUniformHandler::SamplerHandle;
-    using FPCoordTransformIter = GrFragmentProcessor::CoordTransformIter;
+    using UniformHandle         = GrGLSLProgramDataManager::UniformHandle;
+    using SamplerHandle         = GrGLSLUniformHandler::SamplerHandle;
+    using CoordTransformRange   = GrFragmentProcessor::PipelineCoordTransformRange;
 
     struct TransformVar {
         TransformVar() = default;
@@ -56,14 +56,15 @@
      */
     class FPCoordTransformHandler : public SkNoncopyable {
     public:
-        FPCoordTransformHandler(const GrPipeline& pipeline,
-                                SkTArray<TransformVar>* transformedCoordVars)
-                : fIter(pipeline)
-                , fTransformedCoordVars(transformedCoordVars) {}
+        FPCoordTransformHandler(const GrPipeline&, SkTArray<TransformVar>*);
+        ~FPCoordTransformHandler() { SkASSERT(!fIter); }
 
-        ~FPCoordTransformHandler() { SkASSERT(!this->nextCoordTransform());}
+        operator bool() const { return (bool)fIter; }
 
-        const GrCoordTransform* nextCoordTransform();
+        // Gets the current coord transform and its owning GrFragmentProcessor.
+        std::pair<const GrCoordTransform&, const GrFragmentProcessor&> get() const;
+
+        FPCoordTransformHandler& operator++();
 
         // 'args' are constructor params to GrShaderVar.
         template<typename... Args>
@@ -73,10 +74,15 @@
             SkDEBUGCODE(fAddedCoord = true;)
         }
 
+        void omitCoordsForCurrCoordTransform() {
+            SkASSERT(!fAddedCoord);
+            fTransformedCoordVars->push_back();
+            SkDEBUGCODE(fAddedCoord = true;)
+        }
+
     private:
         GrFragmentProcessor::CoordTransformIter fIter;
         SkDEBUGCODE(bool                        fAddedCoord = false;)
-        SkDEBUGCODE(const GrCoordTransform*     fCurr = nullptr;)
         SkTArray<TransformVar>*                 fTransformedCoordVars;
     };
 
@@ -132,14 +138,15 @@
      * GrPrimitiveProcessor parameter is guaranteed to be of the same type and to have an
      * identical processor key as the GrPrimitiveProcessor that created this
      * GrGLSLPrimitiveProcessor.
-     * The subclass may use the transform iterator to perform any setup required for the particular
-     * set of fp transform matrices, such as uploading via uniforms. The iterator will iterate over
-     * the transforms in the same order as the TransformHandler passed to emitCode.
+     * The subclass should use the transform range to perform any setup required for the coord
+     * transforms of the FPs that are part of the same program, such as updating matrix uniforms.
+     * The range will iterate over the transforms in the same order as the TransformHandler passed
+     * to emitCode.
      */
     virtual void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                         FPCoordTransformIter&&) = 0;
+                         const CoordTransformRange&) = 0;
 
-    static SkMatrix GetTransformMatrix(const SkMatrix& localMatrix, const GrCoordTransform&);
+    static SkMatrix GetTransformMatrix(const GrCoordTransform&, const SkMatrix& preMatrix);
 
 protected:
     void setupUniformColor(GrGLSLFPFragmentBuilder* fragBuilder,
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.cpp b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
index 8cdf43d..77f7aee 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.cpp
@@ -68,7 +68,7 @@
     // Because all the texture properties must be consistent between all the dynamic and fixed
     // primProc proxies, we just deal w/ the first set of dynamic proxies or the set of fixed
     // proxies here.
-    const GrTextureProxy* const* primProcProxies = nullptr;
+    const GrSurfaceProxy* const* primProcProxies = nullptr;
     if (fProgramInfo.hasDynamicPrimProcTextures()) {
         primProcProxies = fProgramInfo.dynamicPrimProcTextures(0);
     } else if (fProgramInfo.hasFixedPrimProcTextures()) {
@@ -108,7 +108,7 @@
         SkString name;
         name.printf("TextureSampler_%d", i);
         const auto& sampler = proc.textureSampler(i);
-        SkASSERT(sampler.textureType() == primProcProxies[i]->textureType());
+        SkASSERT(sampler.textureType() == primProcProxies[i]->backendFormat().textureType());
         texSamplers[i] = this->emitSampler(primProcProxies[i],
                                            sampler.samplerState(),
                                            sampler.swizzle(),
@@ -150,9 +150,8 @@
         const GrFragmentProcessor& fp = this->pipeline().getFragmentProcessor(i);
         output = this->emitAndInstallFragProc(fp, i, transformedCoordVarsIdx, **inOut, output,
                                               &glslFragmentProcessors);
-        GrFragmentProcessor::Iter iter(&fp);
-        while (const GrFragmentProcessor* fp = iter.next()) {
-            transformedCoordVarsIdx += fp->numCoordTransforms();
+        for (const auto& subFP : GrFragmentProcessor::FPCRange(fp)) {
+            transformedCoordVarsIdx += subFP.numCoordTransforms();
         }
         **inOut = output;
     }
@@ -185,20 +184,18 @@
     GrGLSLFragmentProcessor* fragProc = fp.createGLSLInstance();
 
     SkSTArray<4, SamplerHandle> texSamplers;
-    GrFragmentProcessor::Iter fpIter(&fp);
     int samplerIdx = 0;
-    while (const auto* subFP = fpIter.next()) {
-        for (int i = 0; i < subFP->numTextureSamplers(); ++i) {
+    for (const auto& subFP : GrFragmentProcessor::FPCRange(fp)) {
+        for (int i = 0; i < subFP.numTextureSamplers(); ++i) {
             SkString name;
             name.printf("TextureSampler_%d", samplerIdx++);
-            const auto& sampler = subFP->textureSampler(i);
+            const auto& sampler = subFP.textureSampler(i);
             texSamplers.emplace_back(this->emitSampler(sampler.proxy(),
                                                        sampler.samplerState(),
                                                        sampler.swizzle(),
                                                        name.c_str()));
         }
     }
-
     const GrGLSLPrimitiveProcessor::TransformVar* coordVars = fTransformedCoordVars.begin() +
                                                               transformedCoordVarsIdx;
     GrGLSLFragmentProcessor::TransformedCoordVars coords(&fp, coordVars);
@@ -248,12 +245,13 @@
     SamplerHandle dstTextureSamplerHandle;
     GrSurfaceOrigin dstTextureOrigin = kTopLeft_GrSurfaceOrigin;
 
-    if (GrTextureProxy* dstTextureProxy = this->pipeline().dstTextureProxy()) {
+    const GrSurfaceProxyView& dstView = this->pipeline().dstProxyView();
+    if (GrTextureProxy* dstTextureProxy = dstView.asTextureProxy()) {
         // GrProcessor::TextureSampler sampler(dstTexture);
-        const GrSwizzle& swizzle = dstTextureProxy->textureSwizzle();
+        const GrSwizzle& swizzle = dstView.swizzle();
         dstTextureSamplerHandle = this->emitSampler(dstTextureProxy, GrSamplerState(),
                                                     swizzle, "DstTextureSampler");
-        dstTextureOrigin = dstTextureProxy->origin();
+        dstTextureOrigin = dstView.origin();
         SkASSERT(dstTextureProxy->textureType() != GrTextureType::kExternal);
     }
 
@@ -278,7 +276,7 @@
     fFS.codeAppend("}");
 }
 
-GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(const GrTextureProxy* texture,
+GrGLSLProgramBuilder::SamplerHandle GrGLSLProgramBuilder::emitSampler(const GrSurfaceProxy* texture,
                                                                       const GrSamplerState& state,
                                                                       const GrSwizzle& swizzle,
                                                                       const char* name) {
diff --git a/src/gpu/glsl/GrGLSLProgramBuilder.h b/src/gpu/glsl/GrGLSLProgramBuilder.h
index dc63aa8..1b82a0c 100644
--- a/src/gpu/glsl/GrGLSLProgramBuilder.h
+++ b/src/gpu/glsl/GrGLSLProgramBuilder.h
@@ -10,7 +10,6 @@
 
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrGeometryProcessor.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTarget.h"
 #include "src/gpu/GrRenderTargetPriv.h"
@@ -22,6 +21,7 @@
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 #include "src/gpu/glsl/GrGLSLXferProcessor.h"
 
+class GrProgramDesc;
 class GrShaderVar;
 class GrGLSLVaryingHandler;
 class SkString;
@@ -37,7 +37,6 @@
     virtual const GrCaps* caps() const = 0;
     const GrShaderCaps* shaderCaps() const { return this->caps()->shaderCaps(); }
 
-    int numSamples() const { return fProgramInfo.numSamples(); }
     GrSurfaceOrigin origin() const { return fProgramInfo.origin(); }
     const GrPipeline& pipeline() const { return fProgramInfo.pipeline(); }
     const GrPrimitiveProcessor& primitiveProcessor() const { return fProgramInfo.primProc(); }
@@ -47,8 +46,7 @@
     bool snapVerticesToPixelCenters() const {
         return fProgramInfo.pipeline().snapVerticesToPixelCenters();
     }
-    // TODO: remove this usage of the descriptor's header
-    bool hasPointSize() const { return fDesc->hasPointSize(); }
+    bool hasPointSize() const { return fProgramInfo.primitiveType() == GrPrimitiveType::kPoints; }
 
     // TODO: stop passing in the renderTarget for just the sampleLocations
     int effectiveSampleCnt() const {
@@ -163,7 +161,7 @@
                                     SkString output,
                                     SkTArray<std::unique_ptr<GrGLSLFragmentProcessor>>*);
     void emitAndInstallXferProc(const SkString& colorIn, const SkString& coverageIn);
-    SamplerHandle emitSampler(const GrTextureProxy*, const GrSamplerState&, const GrSwizzle&,
+    SamplerHandle emitSampler(const GrSurfaceProxy*, const GrSamplerState&, const GrSwizzle&,
                               const char* name);
     bool checkSamplerCounts();
 
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index 7675744..b1e5499 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -62,7 +62,7 @@
 
 static inline void append_texture_swizzle(SkString* out, GrSwizzle swizzle) {
     if (swizzle != GrSwizzle::RGBA()) {
-        out->appendf(".%s", swizzle.c_str());
+        out->appendf(".%s", swizzle.asString().c_str());
     }
 }
 
diff --git a/src/gpu/glsl/GrGLSLUniformHandler.h b/src/gpu/glsl/GrGLSLUniformHandler.h
index f27d961..726195b 100644
--- a/src/gpu/glsl/GrGLSLUniformHandler.h
+++ b/src/gpu/glsl/GrGLSLUniformHandler.h
@@ -17,7 +17,7 @@
 
 class GrGLSLProgramBuilder;
 class GrSamplerState;
-class GrTextureProxy;
+class GrSurfaceProxy;
 
 // Handles for program uniforms (other than per-effect uniforms)
 struct GrGLSLBuiltinUniformHandles {
@@ -84,7 +84,7 @@
     // Only called if GrShaderCaps(:textureSwizzleAppliedInShader() == true.
     virtual GrSwizzle samplerSwizzle(SamplerHandle) const = 0;
 
-    virtual SamplerHandle addSampler(const GrTextureProxy*, const GrSamplerState&, const GrSwizzle&,
+    virtual SamplerHandle addSampler(const GrSurfaceProxy*, const GrSamplerState&, const GrSwizzle&,
                                      const char* name, const GrShaderCaps*) = 0;
 
     virtual UniformHandle internalAddUniformArray(uint32_t visibility,
diff --git a/src/gpu/glsl/GrGLSLXferProcessor.cpp b/src/gpu/glsl/GrGLSLXferProcessor.cpp
index 7a04f4b..2535a4b 100644
--- a/src/gpu/glsl/GrGLSLXferProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLXferProcessor.cpp
@@ -113,9 +113,10 @@
         GrGLSLXPFragmentBuilder* x, const GrSwizzle& swizzle, const char* outColor,
         const char* outColorSecondary) const {
     if (GrSwizzle::RGBA() != swizzle) {
-        x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.c_str());
+        x->codeAppendf("%s = %s.%s;", outColor, outColor, swizzle.asString().c_str());
         if (outColorSecondary) {
-            x->codeAppendf("%s = %s.%s;", outColorSecondary, outColorSecondary, swizzle.c_str());
+            x->codeAppendf("%s = %s.%s;", outColorSecondary, outColorSecondary,
+                           swizzle.asString().c_str());
         }
     }
 }
diff --git a/src/gpu/gradients/generated/GrLinearGradientLayout.cpp b/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
index a20b0e5..c5e70d6 100644
--- a/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
@@ -29,9 +29,7 @@
                 fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
         fragBuilder->codeAppendf(
                 "half t = half(%s.x) + 9.9999997473787516e-06;\n%s = half4(t, 1.0, 0.0, 0.0);\n",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                args.fOutputColor);
+                sk_TransformedCoords2D_0.c_str(), args.fOutputColor);
     }
 
 private:
diff --git a/src/gpu/gradients/generated/GrRadialGradientLayout.cpp b/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
index f163943..79fbf48 100644
--- a/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
@@ -28,10 +28,7 @@
         SkString sk_TransformedCoords2D_0 =
                 fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);
         fragBuilder->codeAppendf("half t = half(length(%s));\n%s = half4(t, 1.0, 0.0, 0.0);\n",
-                                 _outer.computeLocalCoordsInVertexShader()
-                                         ? sk_TransformedCoords2D_0.c_str()
-                                         : "_coords",
-                                 args.fOutputColor);
+                                 sk_TransformedCoords2D_0.c_str(), args.fOutputColor);
     }
 
 private:
diff --git a/src/gpu/gradients/generated/GrSweepGradientLayout.cpp b/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
index 6abcbb1..5b5f04a 100644
--- a/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
@@ -39,17 +39,9 @@
                 "atan(-%s.y, length(%s) - %s.x));\n} else {\n    angle = half(atan(-%s.y, "
                 "-%s.x));\n}\nhalf t = ((angle * 0.15915493667125702 + 0.5) + %s) * %s;\n%s = "
                 "half4(t, 1.0, 0.0, 0.0);\n",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                args.fUniformHandler->getUniformCStr(biasVar),
+                sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str(),
+                sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str(),
+                sk_TransformedCoords2D_0.c_str(), args.fUniformHandler->getUniformCStr(biasVar),
                 args.fUniformHandler->getUniformCStr(scaleVar), args.fOutputColor);
     }
 
diff --git a/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp b/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp
index 466b407..61d03bf 100644
--- a/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp
+++ b/src/gpu/gradients/generated/GrTextureGradientColorizer.cpp
@@ -27,7 +27,10 @@
                 "half2 coord = half2(%s.x, 0.5);\n%s = sample(%s, float2(coord)).%s;\n",
                 args.fInputColor, args.fOutputColor,
                 fragBuilder->getProgramBuilder()->samplerVariable(args.fTexSamplers[0]),
-                fragBuilder->getProgramBuilder()->samplerSwizzle(args.fTexSamplers[0]).c_str());
+                fragBuilder->getProgramBuilder()
+                        ->samplerSwizzle(args.fTexSamplers[0])
+                        .asString()
+                        .c_str());
     }
 
 private:
diff --git a/src/gpu/gradients/generated/GrTextureGradientColorizer.h b/src/gpu/gradients/generated/GrTextureGradientColorizer.h
index e15558e..9ed5d3e 100644
--- a/src/gpu/gradients/generated/GrTextureGradientColorizer.h
+++ b/src/gpu/gradients/generated/GrTextureGradientColorizer.h
@@ -16,7 +16,7 @@
 #include "src/gpu/GrFragmentProcessor.h"
 class GrTextureGradientColorizer : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> gradient) {
+    static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrSurfaceProxy> gradient) {
         return std::unique_ptr<GrFragmentProcessor>(new GrTextureGradientColorizer(gradient));
     }
     GrTextureGradientColorizer(const GrTextureGradientColorizer& src);
@@ -25,7 +25,7 @@
     TextureSampler gradient;
 
 private:
-    GrTextureGradientColorizer(sk_sp<GrTextureProxy> gradient)
+    GrTextureGradientColorizer(sk_sp<GrSurfaceProxy> gradient)
             : INHERITED(kGrTextureGradientColorizer_ClassID, kNone_OptimizationFlags)
             , gradient(std::move(gradient), GrSamplerState::ClampBilerp()) {
         this->setTextureSamplerCnt(1);
diff --git a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
index 804a42c..235a137 100644
--- a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
@@ -52,9 +52,8 @@
                 "0:\n        {\n            half r0 = %s.x;\n            @if (%s) {\n              "
                 "  t = length(p) - float(r0);\n            } else {\n                t = "
                 "-length(p) - float(r0);\n       ",
-                _outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str()
-                                                          : "_coords",
-                (int)_outer.type, args.fUniformHandler->getUniformCStr(focalParamsVar),
+                sk_TransformedCoords2D_0.c_str(), (int)_outer.type,
+                args.fUniformHandler->getUniformCStr(focalParamsVar),
                 args.fUniformHandler->getUniformCStr(focalParamsVar),
                 (_outer.isRadiusIncreasing ? "true" : "false"));
         fragBuilder->codeAppendf(
diff --git a/src/gpu/mock/GrMockCaps.cpp b/src/gpu/mock/GrMockCaps.cpp
index 8d798fa..680e69d 100644
--- a/src/gpu/mock/GrMockCaps.cpp
+++ b/src/gpu/mock/GrMockCaps.cpp
@@ -7,6 +7,16 @@
 
 #include "src/gpu/mock/GrMockCaps.h"
 
+#include "src/gpu/GrProgramDesc.h"
+
+GrProgramDesc GrMockCaps::makeDesc(const GrRenderTarget* rt,
+                                   const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    SkDEBUGCODE(bool result =) GrProgramDesc::Build(&desc, rt, programInfo, *this);
+    SkASSERT(result == desc.isValid());
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrMockCaps::getTestingCombinations() const {
     // TODO: need to add compressed formats to this list
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index 457236c..b386571 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -34,10 +34,9 @@
         fShaderCaps->fMaxFragmentSamplers = options.fMaxFragmentSamplers;
         fShaderCaps->fShaderDerivativeSupport = options.fShaderDerivativeSupport;
         fShaderCaps->fDualSourceBlendingSupport = options.fDualSourceBlendingSupport;
-        fShaderCaps->fSampleVariablesSupport = true;
-        fShaderCaps->fSampleVariablesStencilSupport = true;
+        fShaderCaps->fSampleMaskSupport = true;
 
-        this->applyOptionsOverrides(contextOptions);
+        this->finishInitialization(contextOptions);
     }
 
     bool isFormatSRGB(const GrBackendFormat& format) const override {
@@ -144,6 +143,8 @@
         return GrSwizzle();
     }
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<GrCaps::TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index 0825d86..f50a80d 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -56,7 +56,7 @@
                                 GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
                                 const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
                                 const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-                                const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+                                const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     return new GrMockOpsRenderPass(this, rt, origin, colorInfo);
 }
 
@@ -258,14 +258,12 @@
     return new GrMockStencilAttachment(this, width, height, kBits, rt->numSamples());
 }
 
-GrBackendTexture GrMockGpu::onCreateBackendTexture(int w, int h,
+GrBackendTexture GrMockGpu::onCreateBackendTexture(SkISize dimensions,
                                                    const GrBackendFormat& format,
-                                                   GrMipMapped mipMapped,
-                                                   GrRenderable /* renderable */,
-                                                   const SkPixmap /*srcData*/[],
-                                                   int /*numMipLevels*/,
-                                                   const SkColor4f* /* color */,
-                                                   GrProtected /* isProtected */) {
+                                                   GrRenderable,
+                                                   const BackendTextureData*,
+                                                   int numMipLevels,
+                                                   GrProtected) {
     auto colorType = format.asMockColorType();
     if (!this->caps()->isFormatTexturable(format)) {
         return GrBackendTexture();  // invalid
@@ -274,7 +272,8 @@
     GrMockTextureInfo info(colorType, NextExternalTextureID());
 
     fOutstandingTestingOnlyTextureIDs.add(info.fID);
-    return GrBackendTexture(w, h, mipMapped, info);
+    auto mipMapped = numMipLevels > 1 ? GrMipMapped::kYes : GrMipMapped::kNo;
+    return GrBackendTexture(dimensions.width(), dimensions.height(), mipMapped, info);
 }
 
 void GrMockGpu::deleteBackendTexture(const GrBackendTexture& tex) {
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index bbafae0..1fd97cc 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -28,21 +28,26 @@
             GrRenderTarget*, GrSurfaceOrigin, const SkIRect&,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) override;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) override;
 
     GrFence SK_WARN_UNUSED_RESULT insertFence() override { return 0; }
     bool waitFence(GrFence, uint64_t) override { return true; }
     void deleteFence(GrFence) const override {}
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override {
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override {
         return nullptr;
     }
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                            GrResourceProvider::SemaphoreWrapType wrapType,
-                                            GrWrapOwnership ownership) override { return nullptr; }
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override {}
-    void waitSemaphore(sk_sp<GrSemaphore> semaphore) override {}
-    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; }
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(
+            const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType,
+            GrWrapOwnership ownership) override {
+        return nullptr;
+    }
+    void insertSemaphore(GrSemaphore* semaphore) override {}
+    void waitSemaphore(GrSemaphore* semaphore) override {}
+    std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override {
+        return nullptr;
+    }
 
     void submit(GrOpsRenderPass* renderPass) override;
 
@@ -121,19 +126,22 @@
     void onResolveRenderTarget(GrRenderTarget* target, const SkIRect&, GrSurfaceOrigin,
                                ForExternalIO) override {}
 
-    void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+    bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                        const GrFlushInfo& info, const GrPrepareForExternalIORequests&) override {
         if (info.fFinishedProc) {
             info.fFinishedProc(info.fFinishedContext);
         }
+        return true;
     }
 
     GrStencilAttachment* createStencilAttachmentForRenderTarget(
             const GrRenderTarget*, int width, int height, int numStencilSamples) override;
-    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
-                                            GrMipMapped, GrRenderable,
-                                            const SkPixmap srcData[], int numMipLevels,
-                                            const SkColor4f* color, GrProtected) override;
+    GrBackendTexture onCreateBackendTexture(SkISize,
+                                            const GrBackendFormat&,
+                                            GrRenderable,
+                                            const BackendTextureData* data,
+                                            int numMipLevels,
+                                            GrProtected) override;
     void deleteBackendTexture(const GrBackendTexture&) override;
 
 #if GR_TEST_UTILS
diff --git a/src/gpu/mtl/GrMtlBuffer.mm b/src/gpu/mtl/GrMtlBuffer.mm
index fe585fc..304da54 100644
--- a/src/gpu/mtl/GrMtlBuffer.mm
+++ b/src/gpu/mtl/GrMtlBuffer.mm
@@ -41,6 +41,11 @@
         if (@available(macOS 10.11, iOS 9.0, *)) {
             options |= MTLResourceStorageModePrivate;
         }
+#ifdef SK_BUILD_FOR_MAC
+        // Mac requires 4-byte alignment for copies so we need
+        // to ensure we have space for the extra data
+        size = GrSizeAlignUp(size, 4);
+#endif
         fMtlBuffer = size == 0 ? nil :
                 [gpu->device() newBufferWithLength: size
                                            options: options];
@@ -77,7 +82,7 @@
     }
     SkASSERT(fMappedBuffer);
     if (!fIsDynamic) {
-        SkASSERT(srcInBytes == fMappedBuffer.length);
+        SkASSERT(GrSizeAlignUp(srcInBytes, 4) == fMappedBuffer.length);
     }
     memcpy(fMapPtr, src, srcInBytes);
     this->internalUnmap(srcInBytes);
@@ -129,6 +134,10 @@
         if (@available(macOS 10.11, iOS 9.0, *)) {
             options |= MTLResourceStorageModeShared;
         }
+#ifdef SK_BUILD_FOR_MAC
+        // Mac requires 4-byte alignment for copies so we pad this out
+        sizeInBytes = GrSizeAlignUp(sizeInBytes, 4);
+#endif
         fMappedBuffer =
                 [this->mtlGpu()->device() newBufferWithLength: sizeInBytes
                                                       options: options];
@@ -149,9 +158,13 @@
         fMapPtr = nullptr;
         return;
     }
+#ifdef SK_BUILD_FOR_MAC
+    // In both cases the size needs to be 4-byte aligned on Mac
+    sizeInBytes = GrSizeAlignUp(sizeInBytes, 4);
+#endif
     if (fIsDynamic) {
 #ifdef SK_BUILD_FOR_MAC
-        // TODO: need to make sure offset and size have valid alignments.
+        SkASSERT(0 == (fOffset & 0x3));  // should be 4-byte aligned
         [fMtlBuffer didModifyRange: NSMakeRange(fOffset, sizeInBytes)];
 #endif
     } else {
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index 6ffd3c3..9962559 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -65,8 +65,13 @@
         return fPreferredStencilFormat;
     }
 
-    bool canCopyAsBlit(MTLPixelFormat dstFormat, int dstSampleCount, MTLPixelFormat srcFormat,
-                       int srcSampleCount, const SkIRect& srcRect, const SkIPoint& dstPoint,
+    bool canCopyAsBlit(GrSurface* dst, int dstSampleCount, GrSurface* src, int srcSampleCount,
+                       const SkIRect& srcRect, const SkIPoint& dstPoint,
+                       bool areDstSrcSameObj) const;
+
+    bool canCopyAsBlit(MTLPixelFormat dstFormat, int dstSampleCount,
+                       MTLPixelFormat srcFormat, int srcSampleCount,
+                       const SkIRect& srcRect, const SkIPoint& dstPoint,
                        bool areDstSrcSameObj) const;
 
     bool canCopyAsResolve(GrSurface* dst, int dstSampleCount, GrSurface* src, int srcSampleCount,
@@ -85,6 +90,8 @@
     GrSwizzle getTextureSwizzle(const GrBackendFormat&, GrColorType) const override;
     GrSwizzle getOutputSwizzle(const GrBackendFormat&, GrColorType) const override;
 
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index 7511f26..a5fbdef 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -9,7 +9,11 @@
 
 #include "include/core/SkRect.h"
 #include "include/gpu/GrBackendSurface.h"
+#include "src/gpu/GrProcessor.h"
+#include "src/gpu/GrProgramDesc.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrRenderTarget.h"
+#include "src/gpu/GrRenderTargetPriv.h"
 #include "src/gpu/GrRenderTargetProxy.h"
 #include "src/gpu/GrShaderCaps.h"
 #include "src/gpu/GrSurfaceProxy.h"
@@ -30,12 +34,7 @@
     this->initFormatTable();
     this->initStencilFormat(device);
 
-    this->applyOptionsOverrides(contextOptions);
-    fShaderCaps->applyOptionsOverrides(contextOptions);
-
-    // The following are disabled due to the unfinished Metal backend, not because Metal itself
-    // doesn't support it.
-    fCrossContextTextureSupport = false; // GrMtlGpu::prepareTextureForCrossContextUsage() not impl
+    this->finishInitialization(contextOptions);
 }
 
 void GrMtlCaps::initFeatureSet(MTLFeatureSet featureSet) {
@@ -128,6 +127,23 @@
     SK_ABORT("Requested an unsupported feature set");
 }
 
+bool GrMtlCaps::canCopyAsBlit(GrSurface* dst, int dstSampleCount,
+                              GrSurface* src, int srcSampleCount,
+                              const SkIRect& srcRect, const SkIPoint& dstPoint,
+                              bool areDstSrcSameObj) const {
+    id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst);
+    id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src);
+    if (srcTex.framebufferOnly || dstTex.framebufferOnly) {
+        return false;
+    }
+
+    MTLPixelFormat dstFormat = dstTex.pixelFormat;
+    MTLPixelFormat srcFormat = srcTex.pixelFormat;
+
+    return this->canCopyAsBlit(dstFormat, dstSampleCount, srcFormat, srcSampleCount,
+                               srcRect, dstPoint, areDstSrcSameObj);
+}
+
 bool GrMtlCaps::canCopyAsBlit(MTLPixelFormat dstFormat, int dstSampleCount,
                               MTLPixelFormat srcFormat, int srcSampleCount,
                               const SkIRect& srcRect, const SkIPoint& dstPoint,
@@ -185,6 +201,8 @@
     SkASSERT((dstSampleCnt > 0) == SkToBool(dst->asRenderTargetProxy()));
     SkASSERT((srcSampleCnt > 0) == SkToBool(src->asRenderTargetProxy()));
 
+    // TODO: need some way to detect whether the proxy is framebufferOnly
+
     return this->canCopyAsBlit(GrBackendFormatAsMTLPixelFormat(dst->backendFormat()), dstSampleCnt,
                                GrBackendFormatAsMTLPixelFormat(src->backendFormat()), srcSampleCnt,
                                srcRect, dstPoint, dst == src);
@@ -273,8 +291,10 @@
     }
     fSemaphoreSupport = supportsMTLEvent;
 
-    fCrossContextTextureSupport = false;
+    fCrossContextTextureSupport = true;
     fHalfFloatVertexAttributeSupport = true;
+
+    fDynamicStateArrayGeometryProcessorTextureSupport = true;
 }
 
 static bool format_is_srgb(MTLPixelFormat format) {
@@ -950,6 +970,11 @@
         case GrColorType::kAlpha_8xxx:
         case GrColorType::kAlpha_F32xxx:
         case GrColorType::kGray_8xxx:
+        case GrColorType::kRGB_888:
+        case GrColorType::kR_8:
+        case GrColorType::kR_16:
+        case GrColorType::kR_F16:
+        case GrColorType::kGray_F16:
             return kUnknown_GrPixelConfig;
     }
     SkUNREACHABLE;
@@ -1074,6 +1099,52 @@
     return {GrColorType::kUnknown, 0};
 }
 
+/**
+ * For Metal we want to cache the entire pipeline for reuse of draws. The Desc here holds all
+ * the information needed to differentiate one pipeline from another.
+ *
+ * The GrProgramDesc contains all the information need to create the actual shaders for the
+ * pipeline.
+ *
+ * For Metal we need to add to the GrProgramDesc to include the rest of the state on the
+ * pipeline. This includes blending information and primitive type. The pipeline is immutable
+ * so any remaining dynamic state is set via the MtlRenderCmdEncoder.
+ */
+GrProgramDesc GrMtlCaps::makeDesc(const GrRenderTarget* rt,
+                                  const GrProgramInfo& programInfo) const {
+
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    b.add32(programInfo.backendFormat().asMtlFormat());
+
+    b.add32(programInfo.numRasterSamples());
+
+#ifdef SK_DEBUG
+    if (rt && programInfo.pipeline().isStencilEnabled()) {
+        SkASSERT(rt->renderTargetPriv().getStencilAttachment());
+    }
+#endif
+
+    b.add32(programInfo.pipeline().isStencilEnabled()
+                                 ? this->preferredStencilFormat().fInternalFormat
+                                 : MTLPixelFormatInvalid);
+    b.add32((uint32_t)programInfo.pipeline().isStencilEnabled());
+    // Stencil samples don't seem to be tracked in the MTLRenderPipeline
+
+    programInfo.pipeline().genKey(&b, *this);
+
+    b.add32((uint32_t)programInfo.primitiveType());
+
+    return desc;
+}
+
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrMtlCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/mtl/GrMtlDepthStencil.mm b/src/gpu/mtl/GrMtlDepthStencil.mm
index 7434772..8cb584d 100644
--- a/src/gpu/mtl/GrMtlDepthStencil.mm
+++ b/src/gpu/mtl/GrMtlDepthStencil.mm
@@ -75,11 +75,11 @@
     MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
     if (!stencil.isDisabled()) {
         if (stencil.isTwoSided()) {
-            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.front(origin));
-            desc.backFaceStencil = skia_stencil_to_mtl(stencil.back(origin));
+            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.postOriginCCWFace(origin));
+            desc.backFaceStencil = skia_stencil_to_mtl(stencil.postOriginCWFace(origin));
         }
         else {
-            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.frontAndBack());
+            desc.frontFaceStencil = skia_stencil_to_mtl(stencil.singleSidedFace());
             desc.backFaceStencil = desc.frontFaceStencil;
         }
     }
@@ -113,11 +113,11 @@
         memset(&depthStencilKey, 0, sizeof(Key));
     } else {
         if (stencil.isTwoSided()) {
-            skia_stencil_to_key(stencil.front(origin), &depthStencilKey.fFront);
-            skia_stencil_to_key(stencil.back(origin), &depthStencilKey.fBack);
+            skia_stencil_to_key(stencil.postOriginCCWFace(origin), &depthStencilKey.fFront);
+            skia_stencil_to_key(stencil.postOriginCWFace(origin), &depthStencilKey.fBack);
         }
         else {
-            skia_stencil_to_key(stencil.frontAndBack(), &depthStencilKey.fFront);
+            skia_stencil_to_key(stencil.singleSidedFace(), &depthStencilKey.fFront);
             memcpy(&depthStencilKey.fBack, &depthStencilKey.fFront, sizeof(Key::Face));
         }
     }
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 7fd9134..4df71c5 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -80,7 +80,7 @@
             GrRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) override;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) override;
 
     SkSL::Compiler* shaderCompiler() const { return fCompiler.get(); }
 
@@ -90,14 +90,17 @@
     bool waitFence(GrFence, uint64_t) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                            GrResourceProvider::SemaphoreWrapType wrapType,
-                                            GrWrapOwnership ownership) override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
-    void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(
+            const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType,
+            GrWrapOwnership ownership) override;
+    void insertSemaphore(GrSemaphore* semaphore) override;
+    void waitSemaphore(GrSemaphore* semaphore) override;
     void checkFinishProcs() override;
-    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; }
+    std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override {
+        return nullptr;
+    }
 
     // When the Metal backend actually uses indirect command buffers, this function will actually do
     // what it says. For now, every command is encoded directly into the primary command buffer, so
@@ -123,10 +126,12 @@
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
-    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
-                                            GrMipMapped, GrRenderable,
-                                            const SkPixmap srcData[], int numMipLevels,
-                                            const SkColor4f* color, GrProtected) override;
+    GrBackendTexture onCreateBackendTexture(SkISize,
+                                            const GrBackendFormat&,
+                                            GrRenderable,
+                                            const BackendTextureData*,
+                                            int numMipLevels,
+                                            GrProtected) override;
 
     sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc,
                                      const GrBackendFormat& format,
@@ -180,7 +185,7 @@
 
     void resolveTexture(id<MTLTexture> colorTexture, id<MTLTexture> resolveTexture);
 
-    void onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
+    bool onFinishFlush(GrSurfaceProxy*[], int n, SkSurface::BackendSurfaceAccess access,
                        const GrFlushInfo& info, const GrPrepareForExternalIORequests&) override;
 
     // Function that uploads data onto textures with private storage mode (GPU access only).
@@ -196,10 +201,12 @@
             const GrRenderTarget*, int width, int height, int numStencilSamples) override;
 
     bool createMtlTextureForBackendSurface(MTLPixelFormat,
-                                           int w, int h, bool texturable,
-                                           bool renderable, GrMipMapped,
-                                           const SkPixmap srcData[], int numMipLevels,
-                                           const SkColor4f* color, GrMtlTextureInfo*);
+                                           SkISize,
+                                           bool texturable,
+                                           bool renderable,
+                                           const BackendTextureData*,
+                                           int numMipLevels,
+                                           GrMtlTextureInfo*);
 
 #if GR_TEST_UTILS
     void testingOnly_startCapture() override;
@@ -217,11 +224,6 @@
 
     GrMtlResourceProvider fResourceProvider;
 
-    // For FenceSync
-    id<MTLSharedEvent>      fSharedEvent API_AVAILABLE(macos(10.14), ios(12.0));
-    MTLSharedEventListener* fSharedEventListener API_AVAILABLE(macos(10.14), ios(12.0));
-    uint64_t                fLatestEvent;
-
     bool fDisconnected;
 
     struct FinishCallback {
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 8e10230..e2da15a 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -120,14 +120,6 @@
         , fDisconnected(false) {
     fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
     fCaps = fMtlCaps;
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        if (fMtlCaps->fenceSyncSupport()) {
-            fSharedEvent = [fDevice newSharedEvent];
-            dispatch_queue_t dispatchQ = dispatch_queue_create("MTLFenceSync", NULL);
-            fSharedEventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:dispatchQ];
-            fLatestEvent = 0;
-        }
-    }
 }
 
 GrMtlGpu::~GrMtlGpu() {
@@ -167,7 +159,7 @@
             GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
             const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     return new GrMtlOpsRenderPass(this, renderTarget, origin, colorInfo, stencilInfo);
 }
 
@@ -193,7 +185,7 @@
     }
 }
 
-void GrMtlGpu::onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess,
+bool GrMtlGpu::onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess,
                              const GrFlushInfo& info, const GrPrepareForExternalIORequests&) {
     bool forceSync = SkToBool(info.fFlags & kSyncCpu_GrFlushFlag) ||
                      (info.fFinishedProc && !this->mtlCaps().fenceSyncSupport());
@@ -219,6 +211,7 @@
         }
         this->submitCommandBuffer(kSkip_SyncQueue);
     }
+    return true;
 }
 
 void GrMtlGpu::checkFinishProcs() {
@@ -285,9 +278,8 @@
     size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
 
     SkTArray<size_t> individualMipOffsets(mipLevelCount);
-    size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bpp, width, height,
-                                                                 &individualMipOffsets,
-                                                                 mipLevelCount);
+    size_t combinedBufferSize = GrComputeTightCombinedBufferSize(
+            bpp, {width, height}, &individualMipOffsets, mipLevelCount);
     SkASSERT(combinedBufferSize);
 
     size_t bufferOffset;
@@ -740,31 +732,29 @@
     return true;
 }
 
-static GrPixelConfig mtl_format_to_pixelconfig(MTLPixelFormat format) {
+// Used to "clear" a backend texture to a constant color by transferring.
+static GrColorType mtl_format_to_backend_tex_clear_colortype(MTLPixelFormat format) {
     switch(format) {
-        case MTLPixelFormatA8Unorm:         return kAlpha_8_GrPixelConfig;
-        case MTLPixelFormatR8Unorm:         return kAlpha_8_GrPixelConfig;
+        case MTLPixelFormatA8Unorm:         return GrColorType::kAlpha_8;
+        case MTLPixelFormatR8Unorm:         return GrColorType::kR_8;
 
 #ifdef SK_BUILD_FOR_IOS
-        case MTLPixelFormatB5G6R5Unorm:     return kRGB_565_GrPixelConfig;
-        case MTLPixelFormatABGR4Unorm:      return kRGBA_4444_GrPixelConfig;
+        case MTLPixelFormatB5G6R5Unorm:     return GrColorType::kBGR_565;
+        case MTLPixelFormatABGR4Unorm:      return GrColorType::kABGR_4444;
 #endif
-        case MTLPixelFormatRGBA8Unorm:      return kRGBA_8888_GrPixelConfig;
-        case MTLPixelFormatRGBA8Unorm_sRGB: return kSRGBA_8888_GrPixelConfig;
+        case MTLPixelFormatRGBA8Unorm:      return GrColorType::kRGBA_8888;
+        case MTLPixelFormatRGBA8Unorm_sRGB: return GrColorType::kRGBA_8888_SRGB;
 
-#ifdef SK_BUILD_FOR_IOS
-        case MTLPixelFormatETC2_RGB8:       return kRGB_ETC1_GrPixelConfig;
-#endif
-        case MTLPixelFormatRG8Unorm:        return kRG_88_GrPixelConfig;
-        case MTLPixelFormatBGRA8Unorm:      return kBGRA_8888_GrPixelConfig;
-        case MTLPixelFormatRGB10A2Unorm:    return kRGBA_1010102_GrPixelConfig;
-        case MTLPixelFormatR16Float:        return kAlpha_half_GrPixelConfig;
-        case MTLPixelFormatRGBA16Float:     return kRGBA_half_GrPixelConfig;
-        case MTLPixelFormatR16Unorm:        return kAlpha_16_GrPixelConfig;
-        case MTLPixelFormatRG16Unorm:       return kRG_1616_GrPixelConfig;
-        case MTLPixelFormatRGBA16Unorm:     return kRGBA_16161616_GrPixelConfig;
-        case MTLPixelFormatRG16Float:       return kRG_half_GrPixelConfig;
-        default:                            return kUnknown_GrPixelConfig;
+        case MTLPixelFormatRG8Unorm:        return GrColorType::kRG_88;
+        case MTLPixelFormatBGRA8Unorm:      return GrColorType::kBGRA_8888;
+        case MTLPixelFormatRGB10A2Unorm:    return GrColorType::kRGBA_1010102;
+        case MTLPixelFormatR16Float:        return GrColorType::kR_F16;
+        case MTLPixelFormatRGBA16Float:     return GrColorType::kRGBA_F16;
+        case MTLPixelFormatR16Unorm:        return GrColorType::kR_16;
+        case MTLPixelFormatRG16Unorm:       return GrColorType::kRG_1616;
+        case MTLPixelFormatRGBA16Unorm:     return GrColorType::kRGBA_16161616;
+        case MTLPixelFormatRG16Float:       return GrColorType::kRG_F16;
+        default:                            return GrColorType::kUnknown;
     }
 
     SkUNREACHABLE;
@@ -785,14 +775,15 @@
 }
 
 bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
-                                                 int w, int h, bool texturable,
-                                                 bool renderable, GrMipMapped mipMapped,
-                                                 const SkPixmap srcData[], int numMipLevels,
-                                                 const SkColor4f* color, GrMtlTextureInfo* info) {
+                                                 SkISize dimensions,
+                                                 bool texturable,
+                                                 bool renderable,
+                                                 const BackendTextureData* data,
+                                                 int numMipLevels,
+                                                 GrMtlTextureInfo* info) {
     SkASSERT(texturable || renderable);
     if (!texturable) {
-        SkASSERT(GrMipMapped::kNo == mipMapped);
-        SkASSERT(!srcData && !numMipLevels);
+        SkASSERT(!data && numMipLevels == 1);
     }
 
 #ifdef SK_BUILD_FOR_IOS
@@ -807,23 +798,15 @@
         return false;
     }
 
-    if (!check_max_blit_width(w)) {
+    if (!check_max_blit_width(dimensions.width())) {
         return false;
     }
 
-    int mipLevelCount = 1;
-    if (srcData) {
-        SkASSERT(numMipLevels > 0);
-        mipLevelCount = numMipLevels;
-    } else if (GrMipMapped::kYes == mipMapped) {
-        mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
-    }
-
-    bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
+    bool mipmapped = numMipLevels > 1;
     MTLTextureDescriptor* desc =
             [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
-                                                               width: w
-                                                              height: h
+                                                               width: dimensions.width()
+                                                              height: dimensions.height()
                                                            mipmapped: mipmapped];
     if (@available(macOS 10.11, iOS 9.0, *)) {
         desc.storageMode = MTLStorageModePrivate;
@@ -832,7 +815,7 @@
     }
     id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
 
-    if (!srcData && !color) {
+    if (!data) {
         info->fTexture.reset(GrRetainPtrFromId(testTexture));
 
         return true;
@@ -841,11 +824,6 @@
     // Create the transfer buffer
     size_t bytesPerPixel = fMtlCaps->bytesPerPixel(format);
 
-    SkTArray<size_t> individualMipOffsets(mipLevelCount);
-    size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, w, h,
-                                                                 &individualMipOffsets,
-                                                                 mipLevelCount);
-
     NSUInteger options = 0;  // TODO: consider other options here
     if (@available(macOS 10.11, iOS 9.0, *)) {
 #ifdef SK_BUILD_FOR_MAC
@@ -855,34 +833,51 @@
 #endif
     }
 
-    id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize
-                                                        options: options];
-    if (nil == transferBuffer) {
-        return false;
-    }
-
-    char* buffer = (char*) transferBuffer.contents;
-
-    // Fill buffer with data
-    if (srcData) {
-        copy_src_data(buffer, bytesPerPixel, individualMipOffsets,
-                      srcData, numMipLevels, combinedBufferSize);
-    } else if (color) {
-        GrPixelConfig config = mtl_format_to_pixelconfig(format);
-        auto colorType = GrPixelConfigToColorType(config);
-        SkASSERT(kUnknown_GrPixelConfig != config);
-        GrFillInData(colorType, w, h, individualMipOffsets, buffer, *color);
+    // Create a transfer buffer and fill with data.
+    SkSTArray<16, size_t> individualMipOffsets;
+    id<MTLBuffer> transferBuffer;
+    size_t transferBufferSize;
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
+        transferBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, dimensions,
+                                                              &individualMipOffsets, numMipLevels);
+        transferBuffer = [fDevice newBufferWithLength: transferBufferSize
+                                              options: options];
+        if (nil == transferBuffer) {
+            return false;
+        }
+        char* buffer = (char*)transferBuffer.contents;
+        copy_src_data(buffer, bytesPerPixel, individualMipOffsets, data->pixmaps(), numMipLevels,
+                      transferBufferSize);
+    } else {
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
+        auto colorType = mtl_format_to_backend_tex_clear_colortype(format);
+        if (colorType == GrColorType::kUnknown) {
+            return false;
+        }
+        GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, dimensions);
+        auto rb = ii.minRowBytes();
+        transferBufferSize = rb*dimensions.height();
+        transferBuffer = [fDevice newBufferWithLength: transferBufferSize
+                                              options: options];
+        if (nil == transferBuffer) {
+            return false;
+        }
+        if (!GrClearImage(ii, transferBuffer.contents, rb, data->color())) {
+            return false;
+        }
+        // Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
+        individualMipOffsets.push_back_n(numMipLevels, (size_t)0);
     }
 
     // Transfer buffer contents to texture
-    int currentWidth = w;
-    int currentHeight = h;
+    int currentWidth = dimensions.width();
+    int currentHeight = dimensions.height();
     MTLOrigin origin = MTLOriginMake(0, 0, 0);
 
     id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
     id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
 
-    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
+    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
         size_t trimRowBytes = currentWidth * bytesPerPixel;
         size_t levelSize = trimRowBytes*currentHeight;
 
@@ -901,7 +896,7 @@
         currentHeight = SkTMax(1, currentHeight/2);
     }
 #ifdef SK_BUILD_FOR_MAC
-    [transferBuffer didModifyRange: NSMakeRange(0, combinedBufferSize)];
+    [transferBuffer didModifyRange: NSMakeRange(0, transferBufferSize)];
 #endif
 
     [blitCmdEncoder endEncoding];
@@ -914,30 +909,22 @@
     return true;
 }
 
-GrBackendTexture GrMtlGpu::onCreateBackendTexture(int w, int h,
+GrBackendTexture GrMtlGpu::onCreateBackendTexture(SkISize dimensions,
                                                   const GrBackendFormat& format,
-                                                  GrMipMapped mipMapped,
                                                   GrRenderable renderable,
-                                                  const SkPixmap srcData[], int numMipLevels,
-                                                  const SkColor4f* color,
+                                                  const BackendTextureData* data,
+                                                  int numMipLevels,
                                                   GrProtected isProtected) {
-    SkDEBUGCODE(const GrMtlCaps& caps = this->mtlCaps();)
-
     // GrGpu::createBackendTexture should've ensured these conditions
-    SkASSERT(w >= 1 && w <= caps.maxTextureSize() && h >= 1 && h <= caps.maxTextureSize());
-    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
-    SkASSERT(mipMapped == GrMipMapped::kNo || caps.mipMapSupport());
-
     const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
     GrMtlTextureInfo info;
-    if (!this->createMtlTextureForBackendSurface(mtlFormat,
-                                                 w, h, true,
-                                                 GrRenderable::kYes == renderable, mipMapped,
-                                                 srcData, numMipLevels, color, &info)) {
+    if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, true,
+                                                 GrRenderable::kYes == renderable, data,
+                                                 numMipLevels, &info)) {
         return {};
     }
-
-    GrBackendTexture backendTex(w, h, mipMapped, info);
+    GrMipMapped mipMapped = numMipLevels > 1 ? GrMipMapped::kYes : GrMipMapped::kNo;
+    GrBackendTexture backendTex(dimensions.width(), dimensions.height(), mipMapped, info);
     return backendTex;
 }
 
@@ -978,8 +965,7 @@
     }
 
     GrMtlTextureInfo info;
-    if (!this->createMtlTextureForBackendSurface(format, w, h, false, true,
-                                                 GrMipMapped::kNo, nullptr, 0, nullptr, &info)) {
+    if (!this->createMtlTextureForBackendSurface(format, {w, h}, false, true, nullptr, 1, &info)) {
         return {};
     }
 
@@ -1028,15 +1014,15 @@
 
 void GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                                  const SkIPoint& dstPoint) {
-    id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst);
-    id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src);
-
 #ifdef SK_DEBUG
     int dstSampleCnt = get_surface_sample_cnt(dst);
     int srcSampleCnt = get_surface_sample_cnt(src);
-    SkASSERT(this->mtlCaps().canCopyAsBlit(dstTex.pixelFormat, dstSampleCnt, srcTex.pixelFormat,
-                                           srcSampleCnt, srcRect, dstPoint, dst == src));
+    SkASSERT(this->mtlCaps().canCopyAsBlit(dst, dstSampleCnt, src, srcSampleCnt,
+                                           srcRect, dstPoint, dst == src));
 #endif
+    id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst);
+    id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src);
+
     id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
     [blitCmdEncoder copyFromTexture: srcTex
                         sourceSlice: 0
@@ -1053,9 +1039,6 @@
                              const SkIPoint& dstPoint) {
     SkASSERT(!src->isProtected() && !dst->isProtected());
 
-    MTLPixelFormat dstFormat = GrBackendFormatAsMTLPixelFormat(dst->backendFormat());
-    MTLPixelFormat srcFormat = GrBackendFormatAsMTLPixelFormat(src->backendFormat());
-
     int dstSampleCnt = get_surface_sample_cnt(dst);
     int srcSampleCnt = get_surface_sample_cnt(src);
 
@@ -1063,8 +1046,8 @@
     if (this->mtlCaps().canCopyAsResolve(dst, dstSampleCnt, src, srcSampleCnt, srcRect, dstPoint)) {
         this->copySurfaceAsResolve(dst, src);
         success = true;
-    } else if (this->mtlCaps().canCopyAsBlit(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt,
-                                             srcRect, dstPoint, dst == src)) {
+    } else if (this->mtlCaps().canCopyAsBlit(dst, dstSampleCnt, src, srcSampleCnt, srcRect,
+                                             dstPoint, dst == src)) {
         this->copySurfaceAsBlit(dst, src, srcRect, dstPoint);
         success = true;
     }
@@ -1249,14 +1232,8 @@
 
 GrFence SK_WARN_UNUSED_RESULT GrMtlGpu::insertFence() {
     GrMtlCommandBuffer* cmdBuffer = this->commandBuffer();
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        ++fLatestEvent;
-        cmdBuffer->encodeSignalEvent(fSharedEvent, fLatestEvent);
-
-        return fLatestEvent;
-    }
-    // If MTLSharedEvent isn't available, we create a semaphore and signal it
-    // within the current command buffer's completion handler.
+    // We create a semaphore and signal it within the current
+    // command buffer's completion handler.
     dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
     cmdBuffer->addCompletedHandler(^(id <MTLCommandBuffer>commandBuffer) {
         dispatch_semaphore_signal(semaphore);
@@ -1267,61 +1244,44 @@
 }
 
 bool GrMtlGpu::waitFence(GrFence fence, uint64_t timeout) {
-    dispatch_semaphore_t semaphore;
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        semaphore = dispatch_semaphore_create(0);
+    const void* cfFence = (const void*) fence;
+    dispatch_semaphore_t semaphore = (__bridge dispatch_semaphore_t)cfFence;
 
-        // Add listener for this particular value or greater
-        __block dispatch_semaphore_t block_sema = semaphore;
-        [fSharedEvent notifyListener: fSharedEventListener
-                             atValue: fence
-                               block: ^(id<MTLSharedEvent> sharedEvent, uint64_t value) {
-                                   dispatch_semaphore_signal(block_sema);
-                               }];
-
-    } else {
-        const void* cfFence = (const void*) fence;
-        semaphore = (__bridge dispatch_semaphore_t)cfFence;
-    }
     long result = dispatch_semaphore_wait(semaphore, timeout);
 
     return !result;
 }
 
 void GrMtlGpu::deleteFence(GrFence fence) const {
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        // nothing to delete
-    } else {
-        const void* cfFence = (const void*) fence;
-        // In this case it's easier to release in CoreFoundation than depend on ARC
-        CFRelease(cfFence);
-    }
+    const void* cfFence = (const void*) fence;
+    // In this case it's easier to release in CoreFoundation than depend on ARC
+    CFRelease(cfFence);
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrMtlGpu::makeSemaphore(bool isOwned) {
+std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrMtlGpu::makeSemaphore(bool /*isOwned*/) {
     SkASSERT(this->caps()->semaphoreSupport());
-    return GrMtlSemaphore::Make(this, isOwned);
+    return GrMtlSemaphore::Make(this);
 }
 
-sk_sp<GrSemaphore> GrMtlGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                  GrResourceProvider::SemaphoreWrapType wrapType,
-                                                  GrWrapOwnership ownership) {
+std::unique_ptr<GrSemaphore> GrMtlGpu::wrapBackendSemaphore(
+        const GrBackendSemaphore& semaphore,
+        GrResourceProvider::SemaphoreWrapType wrapType,
+        GrWrapOwnership /*ownership*/) {
     SkASSERT(this->caps()->semaphoreSupport());
-    return GrMtlSemaphore::MakeWrapped(this, semaphore.mtlSemaphore(), semaphore.mtlValue(),
-                                       ownership);
+    return GrMtlSemaphore::MakeWrapped(semaphore.mtlSemaphore(), semaphore.mtlValue());
 }
 
-void GrMtlGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrMtlGpu::insertSemaphore(GrSemaphore* semaphore) {
     if (@available(macOS 10.14, iOS 12.0, *)) {
-        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore.get());
+        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore);
 
         this->commandBuffer()->encodeSignalEvent(mtlSem->event(), mtlSem->value());
     }
 }
 
-void GrMtlGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
+void GrMtlGpu::waitSemaphore(GrSemaphore* semaphore) {
     if (@available(macOS 10.14, iOS 12.0, *)) {
-        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore.get());
+        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore);
 
         this->commandBuffer()->encodeWaitForEvent(mtlSem->event(), mtlSem->value());
     }
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.h b/src/gpu/mtl/GrMtlOpsRenderPass.h
index 702bf29..a308bbe 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.h
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.h
@@ -44,7 +44,7 @@
 private:
     GrGpu* gpu() override { return fGpu; }
 
-    GrMtlPipelineState* prepareDrawState(const GrProgramInfo&, GrPrimitiveType);
+    GrMtlPipelineState* prepareDrawState(const GrProgramInfo&);
 
     void onDraw(const GrProgramInfo& programInfo,
                 const GrMesh mesh[],
diff --git a/src/gpu/mtl/GrMtlOpsRenderPass.mm b/src/gpu/mtl/GrMtlOpsRenderPass.mm
index f587492..39110df 100644
--- a/src/gpu/mtl/GrMtlOpsRenderPass.mm
+++ b/src/gpu/mtl/GrMtlOpsRenderPass.mm
@@ -51,14 +51,11 @@
     fGpu->submitIndirectCommandBuffer(fRenderTarget, fOrigin, &iBounds);
 }
 
-GrMtlPipelineState* GrMtlOpsRenderPass::prepareDrawState(const GrProgramInfo& programInfo,
-                                                         GrPrimitiveType primitiveType) {
+GrMtlPipelineState* GrMtlOpsRenderPass::prepareDrawState(const GrProgramInfo& programInfo) {
     // TODO: resolve textures and regenerate mipmaps as needed
 
     GrMtlPipelineState* pipelineState =
-        fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget,
-                                                                     programInfo,
-                                                                     primitiveType);
+        fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget, programInfo);
     if (!pipelineState) {
         return nullptr;
     }
@@ -76,8 +73,7 @@
 
     SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
 
-    GrPrimitiveType primitiveType = meshes[0].primitiveType();
-    GrMtlPipelineState* pipelineState = this->prepareDrawState(programInfo, primitiveType);
+    GrMtlPipelineState* pipelineState = this->prepareDrawState(programInfo);
     if (!pipelineState) {
         return;
     }
@@ -93,6 +89,7 @@
                                 programInfo.pipeline().getXferProcessor());
 
     bool hasDynamicScissors = programInfo.hasDynamicScissors();
+    bool hasDynamicTextures = programInfo.hasDynamicPrimProcTextures();
 
     if (!programInfo.pipeline().isScissorEnabled()) {
         GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder,
@@ -107,28 +104,25 @@
                                                        programInfo.fixedScissor());
     }
 
+    if (!hasDynamicTextures) {
+        pipelineState->bindTextures(fActiveRenderCmdEncoder);
+    }
+
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
         SkASSERT(nil != fActiveRenderCmdEncoder);
-        if (mesh.primitiveType() != primitiveType) {
-            SkDEBUGCODE(pipelineState = nullptr);
-            primitiveType = mesh.primitiveType();
-            pipelineState = this->prepareDrawState(programInfo, primitiveType);
-            if (!pipelineState) {
-                return;
-            }
-
-            [fActiveRenderCmdEncoder setRenderPipelineState:pipelineState->mtlPipelineState()];
-            pipelineState->setDrawState(fActiveRenderCmdEncoder,
-                                        programInfo.pipeline().outputSwizzle(),
-                                        programInfo.pipeline().getXferProcessor());
-        }
+        SkASSERT(mesh.primitiveType() == programInfo.primitiveType());
 
         if (hasDynamicScissors) {
             GrMtlPipelineState::SetDynamicScissorRectState(fActiveRenderCmdEncoder, fRenderTarget,
                                                            fOrigin,
                                                            programInfo.dynamicScissor(i));
         }
+        if (hasDynamicTextures) {
+            auto meshProxies = programInfo.dynamicPrimProcTextures(i);
+            pipelineState->setTextures(programInfo, meshProxies);
+            pipelineState->bindTextures(fActiveRenderCmdEncoder);
+        }
 
         mesh.sendToGpu(this);
     }
diff --git a/src/gpu/mtl/GrMtlPipelineState.h b/src/gpu/mtl/GrMtlPipelineState.h
index e71aee2..4562591 100644
--- a/src/gpu/mtl/GrMtlPipelineState.h
+++ b/src/gpu/mtl/GrMtlPipelineState.h
@@ -48,6 +48,10 @@
 
     void setData(const GrRenderTarget*, const GrProgramInfo&);
 
+    void setTextures(const GrProgramInfo& programInfo,
+                     const GrSurfaceProxy* const primProcTextures[]);
+    void bindTextures(id<MTLRenderCommandEncoder> renderCmdEncoder);
+
     void setDrawState(id<MTLRenderCommandEncoder>, const GrSwizzle& outputSwizzle,
                       const GrXferProcessor&);
 
@@ -97,7 +101,7 @@
 
     void setRenderTargetState(const GrRenderTarget*, GrSurfaceOrigin);
 
-    void bind(id<MTLRenderCommandEncoder>);
+    void bindUniforms(id<MTLRenderCommandEncoder>);
 
     void setBlendConstants(id<MTLRenderCommandEncoder>, const GrSwizzle&, const GrXferProcessor&);
 
diff --git a/src/gpu/mtl/GrMtlPipelineState.mm b/src/gpu/mtl/GrMtlPipelineState.mm
index 9b4dc40..b596309 100644
--- a/src/gpu/mtl/GrMtlPipelineState.mm
+++ b/src/gpu/mtl/GrMtlPipelineState.mm
@@ -58,36 +58,47 @@
 
 void GrMtlPipelineState::setData(const GrRenderTarget* renderTarget,
                                  const GrProgramInfo& programInfo) {
-
-    // Note: the Metal backend currently only supports fixed primProc textures
-    SkASSERT(!programInfo.hasDynamicPrimProcTextures());
-    auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
-                                                          : nullptr;
-
     this->setRenderTargetState(renderTarget, programInfo.origin());
-    fGeometryProcessor->setData(fDataManager, programInfo.primProc(),
-                                GrFragmentProcessor::CoordTransformIter(programInfo.pipeline()));
+    GrFragmentProcessor::PipelineCoordTransformRange transformRange(programInfo.pipeline());
+    fGeometryProcessor->setData(fDataManager, programInfo.primProc(), transformRange);
+
+    if (!programInfo.hasDynamicPrimProcTextures()) {
+        auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
+                                                              : nullptr;
+        this->setTextures(programInfo, proxies);
+    }
+    fDataManager.resetDirtyBits();
+
+#ifdef SK_DEBUG
+    if (programInfo.pipeline().isStencilEnabled()) {
+        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
+        SkASSERT(renderTarget->renderTargetPriv().numStencilBits() == 8);
+    }
+#endif
+
+    fStencil = programInfo.nonGLStencilSettings();
+}
+
+void GrMtlPipelineState::setTextures(const GrProgramInfo& programInfo,
+                                     const GrSurfaceProxy* const primProcTextures[]) {
     fSamplerBindings.reset();
     for (int i = 0; i < programInfo.primProc().numTextureSamplers(); ++i) {
+        SkASSERT(primProcTextures[i]->asTextureProxy());
         const auto& sampler = programInfo.primProc().textureSampler(i);
-        auto texture = static_cast<GrMtlTexture*>(proxies[i]->peekTexture());
+        auto texture = static_cast<GrMtlTexture*>(primProcTextures[i]->peekTexture());
         fSamplerBindings.emplace_back(sampler.samplerState(), texture, fGpu);
     }
 
-    GrFragmentProcessor::Iter iter(programInfo.pipeline());
+    GrFragmentProcessor::CIter fpIter(programInfo.pipeline());
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        glslFP->setData(fDataManager, *fp);
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            const auto& sampler = fp->textureSampler(i);
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        glslIter->setData(fDataManager, *fpIter);
+        for (int i = 0; i < fpIter->numTextureSamplers(); ++i) {
+            const auto& sampler = fpIter->textureSampler(i);
             fSamplerBindings.emplace_back(sampler.samplerState(), sampler.peekTexture(), fGpu);
         }
-        fp = iter.next();
-        glslFP = glslIter.next();
     }
-    SkASSERT(!fp && !glslFP);
+    SkASSERT(!fpIter && !glslIter);
 
     {
         SkIPoint offset;
@@ -97,36 +108,30 @@
                                 dstTexture, offset);
     }
 
-    if (GrTextureProxy* dstTextureProxy = programInfo.pipeline().dstTextureProxy()) {
+    if (GrTextureProxy* dstTextureProxy = programInfo.pipeline().dstProxyView().asTextureProxy()) {
         fSamplerBindings.emplace_back(GrSamplerState::ClampNearest(),
                                       dstTextureProxy->peekTexture(),
                                       fGpu);
     }
 
     SkASSERT(fNumSamplers == fSamplerBindings.count());
-    fDataManager.resetDirtyBits();
-
-    if (programInfo.pipeline().isStencilEnabled()) {
-        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
-        fStencil.reset(*programInfo.pipeline().getUserStencil(),
-                       programInfo.pipeline().hasStencilClip(),
-                       renderTarget->renderTargetPriv().numStencilBits());
-    }
 }
 
 void GrMtlPipelineState::setDrawState(id<MTLRenderCommandEncoder> renderCmdEncoder,
                                       const GrSwizzle& outputSwizzle,
                                       const GrXferProcessor& xferProcessor) {
     [renderCmdEncoder pushDebugGroup:@"setDrawState"];
-    this->bind(renderCmdEncoder);
+    this->bindUniforms(renderCmdEncoder);
     this->setBlendConstants(renderCmdEncoder, outputSwizzle, xferProcessor);
     this->setDepthStencilState(renderCmdEncoder);
     [renderCmdEncoder popDebugGroup];
 }
 
-void GrMtlPipelineState::bind(id<MTLRenderCommandEncoder> renderCmdEncoder) {
+void GrMtlPipelineState::bindUniforms(id<MTLRenderCommandEncoder> renderCmdEncoder) {
     fDataManager.uploadAndBindUniformBuffers(fGpu, renderCmdEncoder);
+}
 
+void GrMtlPipelineState::bindTextures(id<MTLRenderCommandEncoder> renderCmdEncoder) {
     SkASSERT(fNumSamplers == fSamplerBindings.count());
     for (int index = 0; index < fNumSamplers; ++index) {
         [renderCmdEncoder setFragmentTexture: fSamplerBindings[index].fTexture
@@ -197,15 +202,16 @@
     if (!fStencil.isDisabled()) {
         if (fStencil.isTwoSided()) {
             if (@available(macOS 10.11, iOS 9.0, *)) {
-                [renderCmdEncoder setStencilFrontReferenceValue:fStencil.front(origin).fRef
-                                             backReferenceValue:fStencil.back(origin).fRef];
+                [renderCmdEncoder
+                        setStencilFrontReferenceValue:fStencil.postOriginCCWFace(origin).fRef
+                        backReferenceValue:fStencil.postOriginCWFace(origin).fRef];
             } else {
                 // Two-sided stencil not supported on older versions of iOS
                 // TODO: Find a way to recover from this
                 SkASSERT(false);
             }
         } else {
-            [renderCmdEncoder setStencilReferenceValue:fStencil.frontAndBack().fRef];
+            [renderCmdEncoder setStencilReferenceValue:fStencil.singleSidedFace().fRef];
         }
     }
     [renderCmdEncoder setDepthStencilState:state->mtlDepthStencil()];
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.h b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
index fda1e65..bfa0c45 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.h
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.h
@@ -9,7 +9,6 @@
 #define GrMtlPipelineStateBuilder_DEFINED
 
 #include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/gpu/mtl/GrMtlUniformHandler.h"
 #include "src/gpu/mtl/GrMtlVaryingHandler.h"
@@ -17,36 +16,14 @@
 
 #import <Metal/Metal.h>
 
+class GrProgramDesc;
 class GrProgramInfo;
+class GrMtlCaps;
 class GrMtlGpu;
 class GrMtlPipelineState;
 
 class GrMtlPipelineStateBuilder : public GrGLSLProgramBuilder {
 public:
-    /**
-     * For Metal we want to cache the entire pipeline for reuse of draws. The Desc here holds all
-     * the information needed to differentiate one pipeline from another.
-     *
-     * The GrProgramDesc contains all the information need to create the actual shaders for the
-     * pipeline.
-     *
-     * For Metal we need to add to the GrProgramDesc to include the rest of the state on the
-     * pipeline. This includes blending information and primitive type. The pipeline is immutable
-     * so any remaining dynamic state is set via the MtlRenderCmdEncoder.
-     */
-    class Desc : public GrProgramDesc {
-    public:
-        static bool Build(Desc*, GrRenderTarget*,
-                          const GrProgramInfo&, GrPrimitiveType, GrMtlGpu* gpu);
-
-        size_t shaderKeyLength() const { return fShaderKeyLength; }
-
-    private:
-        size_t fShaderKeyLength;
-
-        typedef GrProgramDesc INHERITED;
-    };
-
     /** Generates a pipeline state.
      *
      * The GrMtlPipelineState implements what is specified in the GrPipeline and
@@ -58,12 +35,12 @@
     static GrMtlPipelineState* CreatePipelineState(GrMtlGpu*,
                                                    GrRenderTarget*,
                                                    const GrProgramInfo&,
-                                                   Desc*);
+                                                   GrProgramDesc*);
 
 private:
     GrMtlPipelineStateBuilder(GrMtlGpu*, GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
-    GrMtlPipelineState* finalize(GrRenderTarget*, const GrProgramInfo&, Desc*);
+    GrMtlPipelineState* finalize(GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
     const GrCaps* caps() const override;
 
diff --git a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
index 854d50e..dc98db2 100644
--- a/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
+++ b/src/gpu/mtl/GrMtlPipelineStateBuilder.mm
@@ -26,7 +26,7 @@
 GrMtlPipelineState* GrMtlPipelineStateBuilder::CreatePipelineState(GrMtlGpu* gpu,
                                                                    GrRenderTarget* renderTarget,
                                                                    const GrProgramInfo& programInfo,
-                                                                   Desc* desc) {
+                                                                   GrProgramDesc* desc) {
     GrAutoLocaleSetter als("C");
     GrMtlPipelineStateBuilder builder(gpu, renderTarget, programInfo, desc);
 
@@ -296,12 +296,10 @@
 }
 
 static MTLRenderPipelineColorAttachmentDescriptor* create_color_attachment(
-        GrPixelConfig config, const GrPipeline& pipeline) {
+        MTLPixelFormat format, const GrPipeline& pipeline) {
     auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
 
     // pixel format
-    MTLPixelFormat format;
-    SkAssertResult(GrPixelConfigToMTLFormat(config, &format));
     mtlColorAttachment.pixelFormat = format;
 
     // blending
@@ -343,8 +341,8 @@
 
 GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize(GrRenderTarget* renderTarget,
                                                         const GrProgramInfo& programInfo,
-                                                        Desc* desc) {
-    auto pipelineDescriptor = [MTLRenderPipelineDescriptor new];
+                                                        GrProgramDesc* desc) {
+    auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
 
     fVS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n");
     fFS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n");
@@ -391,9 +389,15 @@
     pipelineDescriptor.vertexFunction = vertexFunction;
     pipelineDescriptor.fragmentFunction = fragmentFunction;
     pipelineDescriptor.vertexDescriptor = create_vertex_descriptor(programInfo.primProc());
-    pipelineDescriptor.colorAttachments[0] = create_color_attachment(renderTarget->config(),
+
+    MTLPixelFormat pixelFormat = GrBackendFormatAsMTLPixelFormat(renderTarget->backendFormat());
+    if (pixelFormat == MTLPixelFormatInvalid) {
+        return nullptr;
+    }
+
+    pipelineDescriptor.colorAttachments[0] = create_color_attachment(pixelFormat,
                                                                      programInfo.pipeline());
-    pipelineDescriptor.sampleCount = renderTarget->numSamples();
+    pipelineDescriptor.sampleCount = programInfo.numRasterSamples();
     bool hasStencilAttachment = SkToBool(renderTarget->renderTargetPriv().getStencilAttachment());
     GrMtlCaps* mtlCaps = (GrMtlCaps*)this->caps();
     pipelineDescriptor.stencilAttachmentPixelFormat =
@@ -446,32 +450,3 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-bool GrMtlPipelineStateBuilder::Desc::Build(Desc* desc,
-                                            GrRenderTarget* renderTarget,
-                                            const GrProgramInfo& programInfo,
-                                            GrPrimitiveType primitiveType,
-                                            GrMtlGpu* gpu) {
-    if (!GrProgramDesc::Build(desc, renderTarget, programInfo, primitiveType, gpu)) {
-        return false;
-    }
-
-    GrProcessorKeyBuilder b(&desc->key());
-
-    int keyLength = desc->key().count();
-    SkASSERT(0 == (keyLength % 4));
-    desc->fShaderKeyLength = SkToU32(keyLength);
-
-    b.add32(renderTarget->config());
-    b.add32(renderTarget->numSamples());
-    bool hasStencilAttachment = SkToBool(renderTarget->renderTargetPriv().getStencilAttachment());
-    b.add32(hasStencilAttachment ? gpu->mtlCaps().preferredStencilFormat().fInternalFormat
-                                 : MTLPixelFormatInvalid);
-    b.add32((uint32_t)programInfo.pipeline().isStencilEnabled());
-    // Stencil samples don't seem to be tracked in the MTLRenderPipeline
-
-    b.add32(programInfo.pipeline().getBlendInfoKey());
-
-    b.add32((uint32_t)primitiveType);
-
-    return true;
-}
diff --git a/src/gpu/mtl/GrMtlResourceProvider.h b/src/gpu/mtl/GrMtlResourceProvider.h
index 0b8d219..e568fd3 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.h
+++ b/src/gpu/mtl/GrMtlResourceProvider.h
@@ -11,6 +11,7 @@
 #include "include/private/SkSpinlock.h"
 #include "include/private/SkTArray.h"
 #include "src/core/SkLRUCache.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/mtl/GrMtlDepthStencil.h"
 #include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
 #include "src/gpu/mtl/GrMtlSampler.h"
@@ -25,8 +26,7 @@
     GrMtlResourceProvider(GrMtlGpu* gpu);
 
     GrMtlPipelineState* findOrCreateCompatiblePipelineState(GrRenderTarget*,
-                                                            const GrProgramInfo&,
-                                                            GrPrimitiveType);
+                                                            const GrProgramInfo&);
 
     // Finds or creates a compatible MTLDepthStencilState based on the GrStencilSettings.
     GrMtlDepthStencil* findOrCreateCompatibleDepthStencilState(const GrStencilSettings&,
@@ -52,8 +52,7 @@
         ~PipelineStateCache();
 
         void release();
-        GrMtlPipelineState* refPipelineState(GrRenderTarget*, const GrProgramInfo&,
-                                             GrPrimitiveType);
+        GrMtlPipelineState* refPipelineState(GrRenderTarget*, const GrProgramInfo&);
 
     private:
         struct Entry;
@@ -64,9 +63,9 @@
             }
         };
 
-        SkLRUCache<const GrMtlPipelineStateBuilder::Desc, std::unique_ptr<Entry>, DescHash> fMap;
+        SkLRUCache<const GrProgramDesc, std::unique_ptr<Entry>, DescHash> fMap;
 
-        GrMtlGpu*                    fGpu;
+        GrMtlGpu*                   fGpu;
 
 #ifdef GR_PIPELINE_STATE_CACHE_STATS
         int                         fTotalRequests;
diff --git a/src/gpu/mtl/GrMtlResourceProvider.mm b/src/gpu/mtl/GrMtlResourceProvider.mm
index 9774d26..89be009 100644
--- a/src/gpu/mtl/GrMtlResourceProvider.mm
+++ b/src/gpu/mtl/GrMtlResourceProvider.mm
@@ -39,9 +39,8 @@
 
 GrMtlPipelineState* GrMtlResourceProvider::findOrCreateCompatiblePipelineState(
         GrRenderTarget* renderTarget,
-        const GrProgramInfo& programInfo,
-        GrPrimitiveType primitiveType) {
-    return fPipelineStateCache->refPipelineState(renderTarget, programInfo, primitiveType);
+        const GrProgramInfo& programInfo) {
+    return fPipelineStateCache->refPipelineState(renderTarget, programInfo);
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////
@@ -135,16 +134,15 @@
 
 GrMtlPipelineState* GrMtlResourceProvider::PipelineStateCache::refPipelineState(
         GrRenderTarget* renderTarget,
-        const GrProgramInfo& programInfo,
-        GrPrimitiveType primType) {
+        const GrProgramInfo& programInfo) {
 #ifdef GR_PIPELINE_STATE_CACHE_STATS
     ++fTotalRequests;
 #endif
 
-    // TODO: unify GL, VK and Mtl
-    // Get GrMtlProgramDesc
-    GrMtlPipelineStateBuilder::Desc desc;
-    if (!GrMtlPipelineStateBuilder::Desc::Build(&desc, renderTarget, programInfo, primType, fGpu)) {
+    const GrMtlCaps& caps = fGpu->mtlCaps();
+
+    GrProgramDesc desc = caps.makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(fGpu->caps(), "Failed to build mtl program descriptor!\n");
         return nullptr;
     }
@@ -262,6 +260,10 @@
 }
 
 id<MTLBuffer> GrMtlResourceProvider::getDynamicBuffer(size_t size, size_t* offset) {
+#ifdef SK_BUILD_FOR_MAC
+    // Mac requires 4-byte alignment for didModifyRange:
+    size = GrSizeAlignUp(size, 4);
+#endif
     id<MTLBuffer> buffer = fBufferSuballocator->getAllocation(size, offset);
     if (buffer) {
         return buffer;
diff --git a/src/gpu/mtl/GrMtlSemaphore.h b/src/gpu/mtl/GrMtlSemaphore.h
index 5a5ad9c..1991c9a 100644
--- a/src/gpu/mtl/GrMtlSemaphore.h
+++ b/src/gpu/mtl/GrMtlSemaphore.h
@@ -19,12 +19,11 @@
 
 class GrMtlSemaphore : public GrSemaphore {
 public:
-    static sk_sp<GrMtlSemaphore> Make(GrMtlGpu* gpu, bool isOwned);
+    static std::unique_ptr<GrMtlSemaphore> Make(GrMtlGpu* gpu);
 
-    static sk_sp<GrMtlSemaphore> MakeWrapped(GrMtlGpu* gpu,
-                                             GrMTLHandle event,
-                                             uint64_t value,
-                                             GrWrapOwnership ownership);
+    static std::unique_ptr<GrMtlSemaphore> MakeWrapped(GrMTLHandle event, uint64_t value);
+
+    ~GrMtlSemaphore() override {}
 
     id<MTLEvent> event() const API_AVAILABLE(macos(10.14), ios(12.0)) { return fEvent; }
     uint64_t value() const { return fValue; }
@@ -32,11 +31,9 @@
     GrBackendSemaphore backendSemaphore() const override;
 
 private:
-    GrMtlSemaphore(GrMtlGpu* gpu, id<MTLEvent> event,
-                   uint64_t value, bool isOwned) API_AVAILABLE(macos(10.14), ios(12.0));
+    GrMtlSemaphore(id<MTLEvent> event, uint64_t value) API_AVAILABLE(macos(10.14), ios(12.0));
 
-    void onRelease() override;
-    void onAbandon() override;
+    void setIsOwned() override {}
 
     id<MTLEvent> fEvent API_AVAILABLE(macos(10.14), ios(12.0));
     uint64_t     fValue;
diff --git a/src/gpu/mtl/GrMtlSemaphore.mm b/src/gpu/mtl/GrMtlSemaphore.mm
index 3eb31ac..7757520 100644
--- a/src/gpu/mtl/GrMtlSemaphore.mm
+++ b/src/gpu/mtl/GrMtlSemaphore.mm
@@ -5,57 +5,38 @@
  * found in the LICENSE file.
  */
 
-#include "src/gpu/mtl/GrMtlGpu.h"
 #include "src/gpu/mtl/GrMtlSemaphore.h"
 
+#include "src/gpu/mtl/GrMtlGpu.h"
+
 #if !__has_feature(objc_arc)
 #error This file must be compiled with Arc. Use -fobjc-arc flag
 #endif
 
-sk_sp<GrMtlSemaphore> GrMtlSemaphore::Make(GrMtlGpu* gpu, bool isOwned) {
+std::unique_ptr<GrMtlSemaphore> GrMtlSemaphore::Make(GrMtlGpu* gpu) {
     if (@available(macOS 10.14, iOS 12.0, *)) {
         id<MTLEvent> event = [gpu->device() newEvent];
         uint64_t value = 1; // seems like a reasonable starting point
-        return sk_sp<GrMtlSemaphore>(new GrMtlSemaphore(gpu, event, value, isOwned));
+        return std::unique_ptr<GrMtlSemaphore>(new GrMtlSemaphore(event, value));
     } else {
         return nullptr;
     }
 }
 
-sk_sp<GrMtlSemaphore> GrMtlSemaphore::MakeWrapped(GrMtlGpu* gpu,
-                                                  GrMTLHandle event,
-                                                  uint64_t value,
-                                                  GrWrapOwnership ownership) {
+std::unique_ptr<GrMtlSemaphore> GrMtlSemaphore::MakeWrapped(GrMTLHandle event,
+                                                            uint64_t value) {
     // The GrMtlSemaphore will have strong ownership at this point.
     // The GrMTLHandle will subsequently only have weak ownership.
     if (@available(macOS 10.14, iOS 12.0, *)) {
         id<MTLEvent> mtlEvent = (__bridge_transfer id<MTLEvent>)event;
-        auto sema = sk_sp<GrMtlSemaphore>(new GrMtlSemaphore(gpu, mtlEvent, value,
-                                                             kBorrow_GrWrapOwnership != ownership));
-        return sema;
+        return std::unique_ptr<GrMtlSemaphore>(new GrMtlSemaphore(mtlEvent, value));
     } else {
         return nullptr;
     }
 }
 
-GrMtlSemaphore::GrMtlSemaphore(GrMtlGpu* gpu, id<MTLEvent> event, uint64_t value, bool isOwned)
-        : INHERITED(gpu), fEvent(event), fValue(value) {
-    isOwned ? this->registerWithCache(SkBudgeted::kNo)
-            : this->registerWithCacheWrapped(GrWrapCacheable::kNo);
-}
-
-void GrMtlSemaphore::onRelease() {
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        fEvent = nil;
-    }
-    INHERITED::onRelease();
-}
-
-void GrMtlSemaphore::onAbandon() {
-    if (@available(macOS 10.14, iOS 12.0, *)) {
-        fEvent = nil;
-    }
-    INHERITED::onAbandon();
+GrMtlSemaphore::GrMtlSemaphore(id<MTLEvent> event, uint64_t value)
+        : fEvent(event), fValue(value) {
 }
 
 GrBackendSemaphore GrMtlSemaphore::backendSemaphore() const {
diff --git a/src/gpu/mtl/GrMtlTexture.mm b/src/gpu/mtl/GrMtlTexture.mm
index be9736f..7dd5450 100644
--- a/src/gpu/mtl/GrMtlTexture.mm
+++ b/src/gpu/mtl/GrMtlTexture.mm
@@ -25,6 +25,10 @@
                     GrTextureType::k2D, mipMapsStatus)
         , fTexture(texture) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == texture.mipmapLevelCount));
+    if (@available(macOS 10.11, iOS 9.0, *)) {
+        SkASSERT(SkToBool(texture.usage & MTLTextureUsageShaderRead));
+    }
+    SkASSERT(!texture.framebufferOnly);
     this->registerWithCache(budgeted);
     if (GrMtlFormatIsCompressed(texture.pixelFormat)) {
         this->setReadOnly();
@@ -43,6 +47,10 @@
                     GrTextureType::k2D, mipMapsStatus)
         , fTexture(texture) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == texture.mipmapLevelCount));
+    if (@available(macOS 10.11, iOS 9.0, *)) {
+        SkASSERT(SkToBool(texture.usage & MTLTextureUsageShaderRead));
+    }
+    SkASSERT(!texture.framebufferOnly);
     if (ioType == kRead_GrIOType) {
         this->setReadOnly();
     }
@@ -58,6 +66,10 @@
                     GrTextureType::k2D, mipMapsStatus)
         , fTexture(texture) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == texture.mipmapLevelCount));
+    if (@available(macOS 10.11, iOS 9.0, *)) {
+        SkASSERT(SkToBool(texture.usage & MTLTextureUsageShaderRead));
+    }
+    SkASSERT(!texture.framebufferOnly);
 }
 
 sk_sp<GrMtlTexture> GrMtlTexture::MakeNewTexture(GrMtlGpu* gpu, SkBudgeted budgeted,
@@ -69,7 +81,7 @@
         return nullptr;
     }
     if (@available(macOS 10.11, iOS 9.0, *)) {
-        SkASSERT(MTLTextureUsageShaderRead & texture.usage);
+        SkASSERT(SkToBool(texture.usage & MTLTextureUsageShaderRead));
     }
     return sk_sp<GrMtlTexture>(new GrMtlTexture(gpu, budgeted, desc, texture, mipMapsStatus));
 }
@@ -81,7 +93,7 @@
                                                      GrIOType ioType) {
     SkASSERT(nil != texture);
     if (@available(macOS 10.11, iOS 9.0, *)) {
-        SkASSERT(MTLTextureUsageShaderRead & texture.usage);
+        SkASSERT(SkToBool(texture.usage & MTLTextureUsageShaderRead));
     }
     GrMipMapsStatus mipMapsStatus = texture.mipmapLevelCount > 1 ? GrMipMapsStatus::kValid
                                                                  : GrMipMapsStatus::kNotAllocated;
diff --git a/src/gpu/mtl/GrMtlUniformHandler.h b/src/gpu/mtl/GrMtlUniformHandler.h
index bcf15a0..89e3380 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.h
+++ b/src/gpu/mtl/GrMtlUniformHandler.h
@@ -61,7 +61,7 @@
         fUniforms[u.toIndex()].fVisibility |= visibility;
     }
 
-    SamplerHandle addSampler(const GrTextureProxy*,
+    SamplerHandle addSampler(const GrSurfaceProxy*,
                              const GrSamplerState&,
                              const GrSwizzle&,
                              const char* name,
diff --git a/src/gpu/mtl/GrMtlUniformHandler.mm b/src/gpu/mtl/GrMtlUniformHandler.mm
index 89e1263..870d662 100644
--- a/src/gpu/mtl/GrMtlUniformHandler.mm
+++ b/src/gpu/mtl/GrMtlUniformHandler.mm
@@ -242,7 +242,7 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrTextureProxy* texture,
+GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrSurfaceProxy* texture,
                                                                     const GrSamplerState&,
                                                                     const GrSwizzle& swizzle,
                                                                     const char* name,
@@ -252,7 +252,7 @@
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrTextureType type = texture->textureType();
+    GrTextureType type = texture->backendFormat().textureType();
 
     UniformInfo& info = fSamplers.push_back();
     info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 8906d29..9efdd3a 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -537,10 +537,11 @@
 
 class QuadEdgeEffect : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(const SkMatrix& localMatrix, bool usesLocalCoords,
-                                           bool wideColor) {
-        return sk_sp<GrGeometryProcessor>(
-                new QuadEdgeEffect(localMatrix, usesLocalCoords, wideColor));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const SkMatrix& localMatrix,
+                                     bool usesLocalCoords,
+                                     bool wideColor) {
+        return arena->make<QuadEdgeEffect>(localMatrix, usesLocalCoords, wideColor);
     }
 
     ~QuadEdgeEffect() override {}
@@ -610,9 +611,9 @@
 
         void setData(const GrGLSLProgramDataManager& pdman,
                      const GrPrimitiveProcessor& gp,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
-            this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
+            this->setTransformDataHelper(qe.fLocalMatrix, pdman, transformRange);
         }
 
     private:
@@ -628,6 +629,8 @@
     }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     QuadEdgeEffect(const SkMatrix& localMatrix, bool usesLocalCoords, bool wideColor)
             : INHERITED(kQuadEdgeEffect_ClassID)
             , fLocalMatrix(localMatrix)
@@ -653,11 +656,11 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
     // Doesn't work without derivative instructions.
     return d->caps()->shaderCaps()->shaderDerivativeSupport()
-                   ? QuadEdgeEffect::Make(GrTest::TestMatrix(d->fRandom), d->fRandom->nextBool(),
-                                          d->fRandom->nextBool())
+                   ? QuadEdgeEffect::Make(d->allocator(), GrTest::TestMatrix(d->fRandom),
+                                          d->fRandom->nextBool(), d->fRandom->nextBool())
                    : nullptr;
 }
 #endif
@@ -740,8 +743,9 @@
         }
 
         // Setup GrGeometryProcessor
-        sk_sp<GrGeometryProcessor> quadProcessor(
-                QuadEdgeEffect::Make(invert, fHelper.usesLocalCoords(), fWideColor));
+        GrGeometryProcessor* quadProcessor = QuadEdgeEffect::Make(target->allocator(), invert,
+                                                                  fHelper.usesLocalCoords(),
+                                                                  fWideColor);
         const size_t kVertexStride = quadProcessor->vertexStride();
 
         // TODO generate all segments for all paths and use one vertex buffer
@@ -812,7 +816,7 @@
                 firstIndex += draw.fIndexCnt;
                 firstVertex += draw.fVertexCnt;
             }
-            target->recordDraw(quadProcessor, meshes, draws.count());
+            target->recordDraw(quadProcessor, meshes, draws.count(), GrPrimitiveType::kTriangles);
         }
     }
 
diff --git a/src/gpu/ops/GrAAConvexTessellator.cpp b/src/gpu/ops/GrAAConvexTessellator.cpp
index 9952491..86aa54c 100644
--- a/src/gpu/ops/GrAAConvexTessellator.cpp
+++ b/src/gpu/ops/GrAAConvexTessellator.cpp
@@ -61,12 +61,20 @@
 
 static bool points_are_colinear_and_b_is_middle(const SkPoint& a, const SkPoint& b,
                                                 const SkPoint& c) {
-    // 'area' is twice the area of the triangle with corners a, b, and c.
-    SkScalar area = a.fX * (b.fY - c.fY) + b.fX * (c.fY - a.fY) + c.fX * (a.fY - b.fY);
-    if (SkScalarAbs(area) >= 2 * kCloseSqd) {
+    // First check distance from b to the infinite line through a, c
+    SkVector aToC = c - a;
+    SkVector n = {aToC.fY, -aToC.fX};
+    n.normalize();
+
+    SkScalar distBToLineAC = n.dot(b) - n.dot(a);
+    if (SkScalarAbs(distBToLineAC) >= kClose) {
+        // Too far from the line, cannot be colinear
         return false;
     }
-    return (a - b).dot(b - c) >= 0;
+
+    // b is colinear, but it may not be in the line segment between a and c. It's in the middle if
+    // both the angle at a and the angle at c are acute.
+    return aToC.dot(b - a) > 0 && aToC.dot(c - b) > 0;
 }
 
 int GrAAConvexTessellator::addPt(const SkPoint& pt,
@@ -369,7 +377,7 @@
 }
 
 bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) {
-    SkASSERT(SkPath::kConvex_Convexity == path.getConvexity());
+    SkASSERT(SkPathConvexityType::kConvex == path.getConvexityType());
 
     SkRect bounds = path.getBounds();
     m.mapRect(&bounds);
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index f78c672..05f1431 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -962,7 +962,7 @@
 
     // do lines first
     if (lineCount) {
-        sk_sp<GrGeometryProcessor> lineGP;
+        GrGeometryProcessor* lineGP;
         {
             using namespace GrDefaultGeoProcFactory;
 
@@ -970,22 +970,21 @@
             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
                                                               : LocalCoords::kUnused_Type);
             localCoords.fMatrix = geometryProcessorLocalM;
-            lineGP = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+            lineGP = GrDefaultGeoProcFactory::Make(target->allocator(), target->caps().shaderCaps(),
                                                    color, Coverage::kAttribute_Type, localCoords,
                                                    *geometryProcessorViewM);
         }
 
         sk_sp<const GrBuffer> linesIndexBuffer = get_lines_index_buffer(target->resourceProvider());
 
-        sk_sp<const GrBuffer> vertexBuffer;
-        int firstVertex;
-
         SkASSERT(sizeof(LineVertex) == lineGP->vertexStride());
-        int vertexCount = kLineSegNumVertices * lineCount;
-        LineVertex* verts = reinterpret_cast<LineVertex*>(target->makeVertexSpace(
-                sizeof(LineVertex), vertexCount, &vertexBuffer, &firstVertex));
 
-        if (!verts|| !linesIndexBuffer) {
+        GrMeshDrawOp::PatternHelper helper(target, GrPrimitiveType::kTriangles, sizeof(LineVertex),
+                                           std::move(linesIndexBuffer), kLineSegNumVertices,
+                                           kIdxsPerLineSeg, lineCount, kLineSegsNumInIdxBuffer);
+
+        LineVertex* verts = reinterpret_cast<LineVertex*>(helper.vertices());
+        if (!verts) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
@@ -994,29 +993,25 @@
             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
         }
 
-        GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
-        mesh->setIndexedPatterned(std::move(linesIndexBuffer), kIdxsPerLineSeg, kLineSegNumVertices,
-                                  lineCount, kLineSegsNumInIdxBuffer);
-        mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(lineGP), mesh);
+        helper.recordDraw(target, lineGP);
     }
 
     if (quadCount || conicCount) {
-        sk_sp<GrGeometryProcessor> quadGP(GrQuadEffect::Make(this->color(),
-                                                             *geometryProcessorViewM,
-                                                             GrClipEdgeType::kHairlineAA,
-                                                             target->caps(),
-                                                             *geometryProcessorLocalM,
-                                                             fHelper.usesLocalCoords(),
-                                                             this->coverage()));
+        GrGeometryProcessor* quadGP = GrQuadEffect::Make(target->allocator(), this->color(),
+                                                         *geometryProcessorViewM,
+                                                         GrClipEdgeType::kHairlineAA,
+                                                         target->caps(),
+                                                         *geometryProcessorLocalM,
+                                                         fHelper.usesLocalCoords(),
+                                                         this->coverage());
 
-        sk_sp<GrGeometryProcessor> conicGP(GrConicEffect::Make(this->color(),
-                                                               *geometryProcessorViewM,
-                                                               GrClipEdgeType::kHairlineAA,
-                                                               target->caps(),
-                                                               *geometryProcessorLocalM,
-                                                               fHelper.usesLocalCoords(),
-                                                               this->coverage()));
+        GrGeometryProcessor* conicGP = GrConicEffect::Make(target->allocator(), this->color(),
+                                                           *geometryProcessorViewM,
+                                                           GrClipEdgeType::kHairlineAA,
+                                                           target->caps(),
+                                                           *geometryProcessorLocalM,
+                                                           fHelper.usesLocalCoords(),
+                                                           this->coverage());
 
         sk_sp<const GrBuffer> vertexBuffer;
         int firstVertex;
@@ -1053,7 +1048,7 @@
             mesh->setIndexedPatterned(quadsIndexBuffer, kIdxsPerQuad, kQuadNumVertices, quadCount,
                                       kQuadsNumInIdxBuffer);
             mesh->setVertexData(vertexBuffer, firstVertex);
-            target->recordDraw(std::move(quadGP), mesh);
+            target->recordDraw(quadGP, mesh, 1, GrPrimitiveType::kTriangles);
             firstVertex += quadCount * kQuadNumVertices;
         }
 
@@ -1062,7 +1057,7 @@
             mesh->setIndexedPatterned(std::move(quadsIndexBuffer), kIdxsPerQuad, kQuadNumVertices,
                                       conicCount, kQuadsNumInIdxBuffer);
             mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-            target->recordDraw(std::move(conicGP), mesh);
+            target->recordDraw(conicGP, mesh, 1, GrPrimitiveType::kTriangles);
         }
     }
 }
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index d0cbb41..0f3508b 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -96,11 +96,12 @@
     }
 }
 
-static sk_sp<GrGeometryProcessor> create_lines_only_gp(const GrShaderCaps* shaderCaps,
-                                                       bool tweakAlphaForCoverage,
-                                                       const SkMatrix& viewMatrix,
-                                                       bool usesLocalCoords,
-                                                       bool wideColor) {
+static GrGeometryProcessor* create_lines_only_gp(SkArenaAlloc* arena,
+                                                 const GrShaderCaps* shaderCaps,
+                                                 bool tweakAlphaForCoverage,
+                                                 const SkMatrix& viewMatrix,
+                                                 bool usesLocalCoords,
+                                                 bool wideColor) {
     using namespace GrDefaultGeoProcFactory;
 
     Coverage::Type coverageType =
@@ -110,7 +111,8 @@
     Color::Type colorType =
         wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
 
-    return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix);
+    return MakeForDeviceSpace(arena, shaderCaps, colorType, coverageType,
+                              localCoordsType, viewMatrix);
 }
 
 namespace {
@@ -198,7 +200,7 @@
     }
 
 private:
-    void recordDraw(Target* target, sk_sp<const GrGeometryProcessor> gp, int vertexCount,
+    void recordDraw(Target* target, const GrGeometryProcessor* gp, int vertexCount,
                     size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const {
         if (vertexCount == 0 || indexCount == 0) {
             return;
@@ -225,16 +227,17 @@
         mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangles);
     }
 
     void onPrepareDraws(Target* target) override {
         // Setup GrGeometryProcessor
-        sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(target->caps().shaderCaps(),
-                                                           fHelper.compatibleWithCoverageAsAlpha(),
-                                                           this->viewMatrix(),
-                                                           fHelper.usesLocalCoords(),
-                                                           fWideColor));
+        GrGeometryProcessor* gp = create_lines_only_gp(target->allocator(),
+                                                       target->caps().shaderCaps(),
+                                                       fHelper.compatibleWithCoverageAsAlpha(),
+                                                       this->viewMatrix(),
+                                                       fHelper.usesLocalCoords(),
+                                                       fWideColor);
         if (!gp) {
             SkDebugf("Couldn't create a GrGeometryProcessor\n");
             return;
@@ -294,8 +297,7 @@
             indexCount += currentIndices;
         }
         if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) {
-            this->recordDraw(target, std::move(gp), vertexCount, vertexStride, vertices, indexCount,
-                             indices);
+            this->recordDraw(target, gp, vertexCount, vertexStride, vertices, indexCount, indices);
         }
         sk_free(vertices);
         sk_free(indices);
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index bcf4670..d425b71 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -106,7 +106,7 @@
     }
 
     SkRect bounds;
-    geo.fBlob->computeSubRunBounds(&bounds, geo.fRun, geo.fSubRun, geo.fViewMatrix, geo.fX, geo.fY,
+    geo.fBlob->computeSubRunBounds(&bounds, *geo.fSubRunPtr, geo.fViewMatrix, geo.fX, geo.fY,
                                    fNeedsGlyphTransform);
     // We don't have tight bounds on the glyph paths in device space. For the purposes of bounds
     // we treat this as a set of non-AA rects rendered with a texture.
@@ -122,12 +122,11 @@
     SkString str;
 
     for (int i = 0; i < fGeoCount; ++i) {
-        str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
+        str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f\n",
                     i,
                     fGeoData[i].fColor.toBytes_RGBA(),
                     fGeoData[i].fX,
-                    fGeoData[i].fY,
-                    fGeoData[i].fBlob->runCountLimit());
+                    fGeoData[i].fY);
     }
 
     str += fProcessors.dumpProcessors();
@@ -315,12 +314,13 @@
 
     bool vmPerspective = fGeoData[0].fViewMatrix.hasPerspective();
     if (this->usesDistanceFields()) {
-        flushInfo.fGeometryProcessor = this->setupDfProcessor(*target->caps().shaderCaps(),
+        flushInfo.fGeometryProcessor = this->setupDfProcessor(target->allocator(),
+                                                              *target->caps().shaderCaps(),
                                                               proxies, numActiveProxies);
     } else {
         GrSamplerState samplerState = fNeedsGlyphTransform ? GrSamplerState::ClampBilerp()
                                                            : GrSamplerState::ClampNearest();
-        flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
+        flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(target->allocator(),
             *target->caps().shaderCaps(), this->color(), false, proxies, numActiveProxies,
             samplerState, maskFormat, localMatrix, vmPerspective);
     }
@@ -332,7 +332,7 @@
 
     void* vertices = target->makeVertexSpace(vertexStride, glyphCount * kVerticesPerGlyph,
                                              &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset);
-    flushInfo.fIndexBuffer = resourceProvider->refQuadIndexBuffer();
+    flushInfo.fIndexBuffer = resourceProvider->refNonAAQuadIndexBuffer();
     if (!vertices || !flushInfo.fVertexBuffer) {
         SkDebugf("Could not allocate vertices\n");
         return;
@@ -340,16 +340,15 @@
 
     char* currVertex = reinterpret_cast<char*>(vertices);
 
-    SkExclusiveStrikePtr autoGlyphCache;
     // each of these is a SubRun
     for (int i = 0; i < fGeoCount; i++) {
         const Geometry& args = fGeoData[i];
         Blob* blob = args.fBlob;
         // TODO4F: Preserve float colors
         GrTextBlob::VertexRegenerator regenerator(
-                resourceProvider, blob, args.fRun, args.fSubRun, args.fViewMatrix, args.fX, args.fY,
+                resourceProvider, blob, args.fSubRunPtr, args.fViewMatrix, args.fX, args.fY,
                 args.fColor.toBytes_RGBA(), target->deferredUploadTarget(), glyphCache,
-                atlasManager, &autoGlyphCache);
+                atlasManager);
         bool done = false;
         while (!done) {
             GrTextBlob::VertexRegenerator::Result result;
@@ -405,12 +404,16 @@
 
     auto atlasManager = target->atlasManager();
 
-    GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
+    GrGeometryProcessor* gp = flushInfo->fGeometryProcessor;
     GrMaskFormat maskFormat = this->maskFormat();
 
     unsigned int numActiveProxies;
     const sk_sp<GrTextureProxy>* proxies = atlasManager->getProxies(maskFormat, &numActiveProxies);
     SkASSERT(proxies);
+    // Something has gone terribly wrong, bail
+    if (!proxies || 0 == numActiveProxies) {
+        return;
+    }
     if (gp->numTextureSamplers() != (int) numActiveProxies) {
         // During preparation the number of atlas pages has increased.
         // Update the proxies used in the GP to match.
@@ -440,8 +443,8 @@
     mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerGlyph, kVerticesPerGlyph,
                               flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
     mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
-    target->recordDraw(
-            flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState, nullptr);
+    target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState,
+                       nullptr, GrPrimitiveType::kTriangles);
     flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
     flushInfo->fGlyphsToFlush = 0;
 }
@@ -525,9 +528,10 @@
 
 // TODO trying to figure out why lcd is so whack
 // (see comments in GrTextContext::ComputeCanonicalColor)
-sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(const GrShaderCaps& caps,
-                                                           const sk_sp<GrTextureProxy>* proxies,
-                                                           unsigned int numActiveProxies) const {
+GrGeometryProcessor* GrAtlasTextOp::setupDfProcessor(SkArenaAlloc* arena,
+                                                     const GrShaderCaps& caps,
+                                                     const sk_sp<GrTextureProxy>* proxies,
+                                                     unsigned int numActiveProxies) const {
     bool isLCD = this->isLCD();
 
     SkMatrix localMatrix = SkMatrix::I();
@@ -551,7 +555,7 @@
         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
                         redCorrection, greenCorrection, blueCorrection);
-        return GrDistanceFieldLCDTextGeoProc::Make(caps, proxies, numActiveProxies,
+        return GrDistanceFieldLCDTextGeoProc::Make(arena, caps, proxies, numActiveProxies,
                                                    GrSamplerState::ClampBilerp(), widthAdjust,
                                                    fDFGPFlags, localMatrix);
     } else {
@@ -563,11 +567,11 @@
             correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
                                                              fUseGammaCorrectDistanceTable);
         }
-        return GrDistanceFieldA8TextGeoProc::Make(caps, proxies, numActiveProxies,
+        return GrDistanceFieldA8TextGeoProc::Make(arena, caps, proxies, numActiveProxies,
                                                   GrSamplerState::ClampBilerp(),
                                                   correction, fDFGPFlags, localMatrix);
 #else
-        return GrDistanceFieldA8TextGeoProc::Make(caps, proxies, numActiveProxies,
+        return GrDistanceFieldA8TextGeoProc::Make(arena, caps, proxies, numActiveProxies,
                                                   GrSamplerState::ClampBilerp(),
                                                   fDFGPFlags, localMatrix);
 #endif
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 6b821e6..027ea72 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -35,8 +35,7 @@
         Blob*       fBlob;
         SkScalar    fX;
         SkScalar    fY;
-        uint16_t    fRun;
-        uint16_t    fSubRun;
+        GrTextBlob::SubRun* fSubRunPtr;
         SkPMColor4f fColor;
     };
 
@@ -107,7 +106,7 @@
     struct FlushInfo {
         sk_sp<const GrBuffer> fVertexBuffer;
         sk_sp<const GrBuffer> fIndexBuffer;
-        sk_sp<GrGeometryProcessor> fGeometryProcessor;
+        GrGeometryProcessor*  fGeometryProcessor;
         GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fGlyphsToFlush;
         int fVertexOffset;
@@ -153,9 +152,10 @@
 
     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override;
 
-    sk_sp<GrGeometryProcessor> setupDfProcessor(const GrShaderCaps& caps,
-                                                const sk_sp<GrTextureProxy>* proxies,
-                                                unsigned int numActiveProxies) const;
+    GrGeometryProcessor* setupDfProcessor(SkArenaAlloc* arena,
+                                          const GrShaderCaps& caps,
+                                          const sk_sp<GrTextureProxy>* proxies,
+                                          unsigned int numActiveProxies) const;
 
     SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
     int fGeoDataAllocSize;
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index ceba8e6..ecd5b16 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -188,11 +188,12 @@
  * Bounding geometry is rendered and the effect computes coverage based on the fragment's
  * position relative to the dashed line.
  */
-static sk_sp<GrGeometryProcessor> make_dash_gp(const SkPMColor4f&,
-                                               AAMode aaMode,
-                                               DashCap cap,
-                                               const SkMatrix& localMatrix,
-                                               bool usesLocalCoords);
+static GrGeometryProcessor* make_dash_gp(SkArenaAlloc* arena,
+                                         const SkPMColor4f&,
+                                         AAMode aaMode,
+                                         DashCap cap,
+                                         const SkMatrix& localMatrix,
+                                         bool usesLocalCoords);
 
 class DashOp final : public GrMeshDrawOp {
 public:
@@ -330,17 +331,18 @@
         bool isRoundCap = SkPaint::kRound_Cap == cap;
         DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap;
 
-        sk_sp<GrGeometryProcessor> gp;
+        GrGeometryProcessor* gp;
         if (this->fullDash()) {
-            gp = make_dash_gp(this->color(), this->aaMode(), capType, this->viewMatrix(),
-                              fUsesLocalCoords);
+            gp = make_dash_gp(target->allocator(), this->color(), this->aaMode(), capType,
+                              this->viewMatrix(), fUsesLocalCoords);
         } else {
             // Set up the vertex data for the line and start/end dashes
             using namespace GrDefaultGeoProcFactory;
             Color color(this->color());
             LocalCoords::Type localCoordsType =
                     fUsesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
-            gp = MakeForDeviceSpace(target->caps().shaderCaps(),
+            gp = MakeForDeviceSpace(target->allocator(),
+                                    target->caps().shaderCaps(),
                                     color,
                                     Coverage::kSolid_Type,
                                     localCoordsType,
@@ -622,7 +624,7 @@
             }
             rectIndex++;
         }
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -764,10 +766,11 @@
 public:
     typedef SkPathEffect::DashInfo DashInfo;
 
-    static sk_sp<GrGeometryProcessor> Make(const SkPMColor4f&,
-                                           AAMode aaMode,
-                                           const SkMatrix& localMatrix,
-                                           bool usesLocalCoords);
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const SkPMColor4f&,
+                                     AAMode aaMode,
+                                     const SkMatrix& localMatrix,
+                                     bool usesLocalCoords);
 
     const char* name() const override { return "DashingCircleEffect"; }
 
@@ -784,21 +787,23 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class GLDashingCircleEffect;
+    friend class ::SkArenaAlloc; // for access to ctor
+
     DashingCircleEffect(const SkPMColor4f&, AAMode aaMode, const SkMatrix& localMatrix,
                         bool usesLocalCoords);
 
-    SkPMColor4f         fColor;
-    SkMatrix            fLocalMatrix;
-    bool                fUsesLocalCoords;
-    AAMode              fAAMode;
+    SkPMColor4f fColor;
+    SkMatrix    fLocalMatrix;
+    bool        fUsesLocalCoords;
+    AAMode      fAAMode;
 
-    Attribute fInPosition;
-    Attribute fInDashParams;
-    Attribute fInCircleParams;
+    Attribute   fInPosition;
+    Attribute   fInDashParams;
+    Attribute   fInCircleParams;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
-    friend class GLDashingCircleEffect;
     typedef GrGeometryProcessor INHERITED;
 };
 
@@ -815,7 +820,8 @@
                               GrProcessorKeyBuilder*);
 
     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) override;
+                 const CoordTransformRange& transformRange) override;
+
 private:
     UniformHandle fParamUniform;
     UniformHandle fColorUniform;
@@ -888,13 +894,13 @@
 
 void GLDashingCircleEffect::setData(const GrGLSLProgramDataManager& pdman,
                                     const GrPrimitiveProcessor& processor,
-                                    FPCoordTransformIter&& transformIter)  {
+                                    const CoordTransformRange& transformRange) {
     const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>();
     if (dce.color() != fColor) {
         pdman.set4fv(fColorUniform, 1, dce.color().vec());
         fColor = dce.color();
     }
-    this->setTransformDataHelper(dce.localMatrix(), pdman, &transformIter);
+    this->setTransformDataHelper(dce.localMatrix(), pdman, transformRange);
 }
 
 void GLDashingCircleEffect::GenKey(const GrGeometryProcessor& gp,
@@ -909,12 +915,12 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-sk_sp<GrGeometryProcessor> DashingCircleEffect::Make(const SkPMColor4f& color,
-                                                     AAMode aaMode,
-                                                     const SkMatrix& localMatrix,
-                                                     bool usesLocalCoords) {
-    return sk_sp<GrGeometryProcessor>(
-        new DashingCircleEffect(color, aaMode, localMatrix, usesLocalCoords));
+GrGeometryProcessor* DashingCircleEffect::Make(SkArenaAlloc* arena,
+                                               const SkPMColor4f& color,
+                                               AAMode aaMode,
+                                               const SkMatrix& localMatrix,
+                                               bool usesLocalCoords) {
+    return arena->make<DashingCircleEffect>(color, aaMode, localMatrix, usesLocalCoords);
 }
 
 void DashingCircleEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
@@ -930,11 +936,11 @@
                                          AAMode aaMode,
                                          const SkMatrix& localMatrix,
                                          bool usesLocalCoords)
-    : INHERITED(kDashingCircleEffect_ClassID)
-    , fColor(color)
-    , fLocalMatrix(localMatrix)
-    , fUsesLocalCoords(usesLocalCoords)
-    , fAAMode(aaMode) {
+        : INHERITED(kDashingCircleEffect_ClassID)
+        , fColor(color)
+        , fLocalMatrix(localMatrix)
+        , fUsesLocalCoords(usesLocalCoords)
+        , fAAMode(aaMode) {
     fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
     fInDashParams = {"inDashParams", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
     fInCircleParams = {"inCircleParams", kFloat2_GrVertexAttribType, kHalf2_GrSLType};
@@ -944,9 +950,10 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> DashingCircleEffect::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* DashingCircleEffect::TestCreate(GrProcessorTestData* d) {
     AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
-    return DashingCircleEffect::Make(SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
+    return DashingCircleEffect::Make(d->allocator(),
+                                     SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                      aaMode, GrTest::TestMatrix(d->fRandom),
                                      d->fRandom->nextBool());
 }
@@ -969,10 +976,11 @@
 public:
     typedef SkPathEffect::DashInfo DashInfo;
 
-    static sk_sp<GrGeometryProcessor> Make(const SkPMColor4f&,
-                                           AAMode aaMode,
-                                           const SkMatrix& localMatrix,
-                                           bool usesLocalCoords);
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const SkPMColor4f&,
+                                     AAMode aaMode,
+                                     const SkMatrix& localMatrix,
+                                     bool usesLocalCoords);
 
     const char* name() const override { return "DashingEffect"; }
 
@@ -980,7 +988,7 @@
 
     const SkPMColor4f& color() const { return fColor; }
 
-     const SkMatrix& localMatrix() const { return fLocalMatrix; }
+    const SkMatrix& localMatrix() const { return fLocalMatrix; }
 
     bool usesLocalCoords() const { return fUsesLocalCoords; }
 
@@ -989,22 +997,23 @@
     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
 
 private:
+    friend class GLDashingLineEffect;
+    friend class ::SkArenaAlloc; // for access to ctor
+
     DashingLineEffect(const SkPMColor4f&, AAMode aaMode, const SkMatrix& localMatrix,
                       bool usesLocalCoords);
 
-    SkPMColor4f         fColor;
-    SkMatrix            fLocalMatrix;
-    bool                fUsesLocalCoords;
-    AAMode              fAAMode;
+    SkPMColor4f fColor;
+    SkMatrix    fLocalMatrix;
+    bool        fUsesLocalCoords;
+    AAMode      fAAMode;
 
-    Attribute fInPosition;
-    Attribute fInDashParams;
-    Attribute fInRect;
+    Attribute   fInPosition;
+    Attribute   fInDashParams;
+    Attribute   fInRect;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
-    friend class GLDashingLineEffect;
-
     typedef GrGeometryProcessor INHERITED;
 };
 
@@ -1021,7 +1030,7 @@
                               GrProcessorKeyBuilder*);
 
     void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& iter) override;
+                 const CoordTransformRange&) override;
 
 private:
     SkPMColor4f   fColor;
@@ -1112,13 +1121,13 @@
 
 void GLDashingLineEffect::setData(const GrGLSLProgramDataManager& pdman,
                                   const GrPrimitiveProcessor& processor,
-                                  FPCoordTransformIter&& transformIter) {
+                                  const CoordTransformRange& transformRange) {
     const DashingLineEffect& de = processor.cast<DashingLineEffect>();
     if (de.color() != fColor) {
         pdman.set4fv(fColorUniform, 1, de.color().vec());
         fColor = de.color();
     }
-    this->setTransformDataHelper(de.localMatrix(), pdman, &transformIter);
+    this->setTransformDataHelper(de.localMatrix(), pdman, transformRange);
 }
 
 void GLDashingLineEffect::GenKey(const GrGeometryProcessor& gp,
@@ -1133,12 +1142,12 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-sk_sp<GrGeometryProcessor> DashingLineEffect::Make(const SkPMColor4f& color,
-                                                   AAMode aaMode,
-                                                   const SkMatrix& localMatrix,
-                                                   bool usesLocalCoords) {
-    return sk_sp<GrGeometryProcessor>(
-        new DashingLineEffect(color, aaMode, localMatrix, usesLocalCoords));
+GrGeometryProcessor* DashingLineEffect::Make(SkArenaAlloc* arena,
+                                             const SkPMColor4f& color,
+                                             AAMode aaMode,
+                                             const SkMatrix& localMatrix,
+                                             bool usesLocalCoords) {
+    return arena->make<DashingLineEffect>(color, aaMode, localMatrix, usesLocalCoords);
 }
 
 void DashingLineEffect::getGLSLProcessorKey(const GrShaderCaps& caps,
@@ -1168,9 +1177,10 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> DashingLineEffect::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* DashingLineEffect::TestCreate(GrProcessorTestData* d) {
     AAMode aaMode = static_cast<AAMode>(d->fRandom->nextULessThan(GrDashOp::kAAModeCnt));
-    return DashingLineEffect::Make(SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
+    return DashingLineEffect::Make(d->allocator(),
+                                   SkPMColor4f::FromBytes_RGBA(GrRandomColor(d->fRandom)),
                                    aaMode, GrTest::TestMatrix(d->fRandom),
                                    d->fRandom->nextBool());
 }
@@ -1178,11 +1188,12 @@
 #endif
 //////////////////////////////////////////////////////////////////////////////
 
-static sk_sp<GrGeometryProcessor> make_dash_gp(const SkPMColor4f& color,
-                                               AAMode aaMode,
-                                               DashCap cap,
-                                               const SkMatrix& viewMatrix,
-                                               bool usesLocalCoords) {
+static GrGeometryProcessor* make_dash_gp(SkArenaAlloc* arena,
+                                         const SkPMColor4f& color,
+                                         AAMode aaMode,
+                                         DashCap cap,
+                                         const SkMatrix& viewMatrix,
+                                         bool usesLocalCoords) {
     SkMatrix invert;
     if (usesLocalCoords && !viewMatrix.invert(&invert)) {
         SkDebugf("Failed to invert\n");
@@ -1191,9 +1202,9 @@
 
     switch (cap) {
         case kRound_DashCap:
-            return DashingCircleEffect::Make(color, aaMode, invert, usesLocalCoords);
+            return DashingCircleEffect::Make(arena, color, aaMode, invert, usesLocalCoords);
         case kNonRound_DashCap:
-            return DashingLineEffect::Make(color, aaMode, invert, usesLocalCoords);
+            return DashingLineEffect::Make(arena, color, aaMode, invert, usesLocalCoords);
     }
     return nullptr;
 }
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 22d044c..6b8e502 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -67,11 +67,11 @@
 class PathGeoBuilder {
 public:
     PathGeoBuilder(GrPrimitiveType primitiveType, GrMeshDrawOp::Target* target,
-                   sk_sp<const GrGeometryProcessor> geometryProcessor)
+                   const GrGeometryProcessor* geometryProcessor)
             : fPrimitiveType(primitiveType)
             , fTarget(target)
             , fVertexStride(sizeof(SkPoint))
-            , fGeometryProcessor(std::move(geometryProcessor))
+            , fGeometryProcessor(geometryProcessor)
             , fFirstIndex(0)
             , fIndicesInChunk(0)
             , fIndices(nullptr) {
@@ -277,7 +277,7 @@
                                  vertexCount - 1, GrPrimitiveRestart::kNo);
             }
             mesh->setVertexData(std::move(fVertexBuffer), fFirstVertex);
-            fTarget->recordDraw(fGeometryProcessor, mesh);
+            fTarget->recordDraw(fGeometryProcessor, mesh, 1, fPrimitiveType);
         }
 
         fTarget->putBackIndices((size_t)(fIndicesInChunk - indexCount));
@@ -313,7 +313,7 @@
     GrPrimitiveType fPrimitiveType;
     GrMeshDrawOp::Target* fTarget;
     size_t fVertexStride;
-    sk_sp<const GrGeometryProcessor> fGeometryProcessor;
+    const GrGeometryProcessor* fGeometryProcessor;
 
     sk_sp<const GrBuffer> fVertexBuffer;
     int fFirstVertex;
@@ -402,14 +402,15 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp;
+        GrGeometryProcessor* gp;
         {
             using namespace GrDefaultGeoProcFactory;
             Color color(this->color());
             Coverage coverage(this->coverage());
             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
                                                               : LocalCoords::kUnused_Type);
-            gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+            gp = GrDefaultGeoProcFactory::Make(target->allocator(),
+                                               target->caps().shaderCaps(),
                                                color,
                                                coverage,
                                                localCoords,
@@ -431,7 +432,7 @@
         } else {
             primitiveType = GrPrimitiveType::kTriangles;
         }
-        PathGeoBuilder pathGeoBuilder(primitiveType, target, std::move(gp));
+        PathGeoBuilder pathGeoBuilder(primitiveType, target, gp);
 
         // fill buffers
         for (int i = 0; i < instanceCount; i++) {
@@ -539,11 +540,11 @@
             }
             lastPassIsBounds = false;
         } else {
-            switch (path.getFillType()) {
-                case SkPath::kInverseEvenOdd_FillType:
+            switch (path.getNewFillType()) {
+                case SkPathFillType::kInverseEvenOdd:
                     reverse = true;
                     // fallthrough
-                case SkPath::kEvenOdd_FillType:
+                case SkPathFillType::kEvenOdd:
                     passes[0] = &gEOStencilPass;
                     if (stencilOnly) {
                         passCount = 1;
@@ -559,10 +560,10 @@
                     }
                     break;
 
-                case SkPath::kInverseWinding_FillType:
+                case SkPathFillType::kInverseWinding:
                     reverse = true;
                     // fallthrough
-                case SkPath::kWinding_FillType:
+                case SkPathFillType::kWinding:
                     passes[0] = &gWindStencilPass;
                     passCount = 2;
                     if (stencilOnly) {
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index 417e2b4..c8818ec 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -73,17 +73,18 @@
     typedef GrMeshDrawOp INHERITED;
 };
 
-static sk_sp<GrGeometryProcessor> make_gp(const GrShaderCaps* shaderCaps,
-                                          bool hasColors,
-                                          const SkPMColor4f& color,
-                                          const SkMatrix& viewMatrix) {
+static GrGeometryProcessor* make_gp(SkArenaAlloc* arena,
+                                    const GrShaderCaps* shaderCaps,
+                                    bool hasColors,
+                                    const SkPMColor4f& color,
+                                    const SkMatrix& viewMatrix) {
     using namespace GrDefaultGeoProcFactory;
     Color gpColor(color);
     if (hasColors) {
         gpColor.fType = Color::kPremulGrColorAttribute_Type;
     }
 
-    return GrDefaultGeoProcFactory::Make(shaderCaps, gpColor, Coverage::kSolid_Type,
+    return GrDefaultGeoProcFactory::Make(arena, shaderCaps, gpColor, Coverage::kSolid_Type,
                                          LocalCoords::kHasExplicit_Type, viewMatrix);
 }
 
@@ -184,10 +185,11 @@
 
 void DrawAtlasOp::onPrepareDraws(Target* target) {
     // Setup geometry processor
-    sk_sp<GrGeometryProcessor> gp(make_gp(target->caps().shaderCaps(),
-                                          this->hasColors(),
-                                          this->color(),
-                                          this->viewMatrix()));
+    GrGeometryProcessor* gp = make_gp(target->allocator(),
+                                      target->caps().shaderCaps(),
+                                      this->hasColors(),
+                                      this->color(),
+                                      this->viewMatrix());
 
     int instanceCount = fGeoData.count();
     size_t vertexStride = gp->vertexStride();
@@ -208,7 +210,7 @@
         memcpy(vertPtr, args.fVerts.begin(), allocSize);
         vertPtr += allocSize;
     }
-    helper.recordDraw(target, std::move(gp));
+    helper.recordDraw(target, gp);
 }
 
 void DrawAtlasOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
diff --git a/src/gpu/ops/GrDrawOp.h b/src/gpu/ops/GrDrawOp.h
index a9e9752..3385b64 100644
--- a/src/gpu/ops/GrDrawOp.h
+++ b/src/gpu/ops/GrDrawOp.h
@@ -54,6 +54,11 @@
     }
 #endif
 
+#if GR_TEST_UTILS
+    // This is really only intended for GrTextureOp and GrFillRectOp to override
+    virtual int numQuads() const { return -1; }
+#endif
+
 private:
     typedef GrOp INHERITED;
 };
diff --git a/src/gpu/ops/GrDrawPathOp.cpp b/src/gpu/ops/GrDrawPathOp.cpp
index 526ebc0..f2fdbb2 100644
--- a/src/gpu/ops/GrDrawPathOp.cpp
+++ b/src/gpu/ops/GrDrawPathOp.cpp
@@ -50,7 +50,7 @@
     }
     args.fUserStencil = &kCoverPass;
     args.fCaps = &state.caps();
-    args.fDstProxy = state.drawOpArgs().dstProxy();
+    args.fDstProxyView = state.drawOpArgs().dstProxyView();
     args.fOutputSwizzle = state.drawOpArgs().outputSwizzle();
     return args;
 }
@@ -70,8 +70,9 @@
                                 GrPathRendering::FillType fillType, GrStencilSettings* stencil) {
     const GrAppliedClip* appliedClip = flushState.drawOpArgs().appliedClip();
     bool stencilClip = appliedClip && appliedClip->hasStencilClip();
+    GrRenderTarget* rt = flushState.drawOpArgs().proxy()->peekRenderTarget();
     stencil->reset(GrPathRendering::GetStencilPassSettings(fillType), stencilClip,
-                   flushState.drawOpArgs().renderTarget()->renderTargetPriv().numStencilBits());
+                   rt->renderTargetPriv().numStencilBits());
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -88,21 +89,31 @@
 
 void GrDrawPathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
     GrAppliedClip appliedClip = state->detachAppliedClip();
-    GrPipeline::FixedDynamicState fixedDynamicState(appliedClip.scissorState().rect());
+
+    GrPipeline::FixedDynamicState* fixedDynamicState = nullptr, storage;
+
+    if (appliedClip.scissorState().enabled()) {
+        storage.fScissorRect = appliedClip.scissorState().rect();
+        fixedDynamicState = &storage;
+    }
+
     GrPipeline pipeline(this->pipelineInitArgs(*state), this->detachProcessors(),
                         std::move(appliedClip));
     sk_sp<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), this->viewMatrix()));
 
-    GrProgramInfo programInfo(state->drawOpArgs().numSamples(),
-                              state->drawOpArgs().origin(),
-                              pipeline,
-                              *pathProc,
-                              &fixedDynamicState,
-                              nullptr, 0);
+    GrProgramInfo programInfo(state->proxy()->numSamples(),
+                              state->proxy()->numStencilSamples(),
+                              state->proxy()->backendFormat(),
+                              state->view()->origin(),
+                              &pipeline,
+                              pathProc.get(),
+                              fixedDynamicState,
+                              nullptr, 0,
+                              GrPrimitiveType::kPath);
 
     GrStencilSettings stencil;
     init_stencil_pass_settings(*state, this->fillType(), &stencil);
-    state->gpu()->pathRendering()->drawPath(state->drawOpArgs().renderTarget(),
+    state->gpu()->pathRendering()->drawPath(state->drawOpArgs().proxy()->peekRenderTarget(),
                                             programInfo, stencil, fPath.get());
 }
 
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index 4b311b5..102fa3d 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -60,15 +60,16 @@
                      uint16_t* indices) const;
 
     void drawVertices(Target*,
-                      sk_sp<const GrGeometryProcessor>,
+                      const GrGeometryProcessor*,
                       sk_sp<const GrBuffer> vertexBuffer,
                       int firstVertex,
                       sk_sp<const GrBuffer> indexBuffer,
                       int firstIndex);
 
-    sk_sp<GrGeometryProcessor> makeGP(const GrShaderCaps* shaderCaps,
-                                      bool* hasColorAttribute,
-                                      bool* hasLocalCoordAttribute) const;
+    GrGeometryProcessor* makeGP(SkArenaAlloc* arena,
+                                const GrShaderCaps* shaderCaps,
+                                bool* hasColorAttribute,
+                                bool* hasLocalCoordAttribute) const;
 
     GrPrimitiveType primitiveType() const { return fPrimitiveType; }
     bool combinablePrimitive() const {
@@ -229,9 +230,10 @@
     return result;
 }
 
-sk_sp<GrGeometryProcessor> DrawVerticesOp::makeGP(const GrShaderCaps* shaderCaps,
-                                                  bool* hasColorAttribute,
-                                                  bool* hasLocalCoordAttribute) const {
+GrGeometryProcessor* DrawVerticesOp::makeGP(SkArenaAlloc* arena,
+                                            const GrShaderCaps* shaderCaps,
+                                            bool* hasColorAttribute,
+                                            bool* hasLocalCoordAttribute) const {
     using namespace GrDefaultGeoProcFactory;
     LocalCoords::Type localCoordsType;
     if (fHelper.usesLocalCoords()) {
@@ -264,11 +266,12 @@
 
     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
 
-    return GrDefaultGeoProcFactory::Make(shaderCaps,
-                                            color,
-                                            Coverage::kSolid_Type,
-                                            localCoordsType,
-                                            vm);
+    return GrDefaultGeoProcFactory::Make(arena,
+                                         shaderCaps,
+                                         color,
+                                         Coverage::kSolid_Type,
+                                         localCoordsType,
+                                         vm);
 }
 
 void DrawVerticesOp::onPrepareDraws(Target* target) {
@@ -283,9 +286,10 @@
 void DrawVerticesOp::drawVolatile(Target* target) {
     bool hasColorAttribute;
     bool hasLocalCoordsAttribute;
-    sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
-                                                 &hasColorAttribute,
-                                                 &hasLocalCoordsAttribute);
+    GrGeometryProcessor* gp = this->makeGP(target->allocator(),
+                                           target->caps().shaderCaps(),
+                                           &hasColorAttribute,
+                                           &hasLocalCoordsAttribute);
 
     // Allocate buffers.
     size_t vertexStride = gp->vertexStride();
@@ -316,8 +320,7 @@
                       indices);
 
     // Draw the vertices.
-    this->drawVertices(target, std::move(gp), std::move(vertexBuffer), firstVertex, indexBuffer,
-                       firstIndex);
+    this->drawVertices(target, gp, std::move(vertexBuffer), firstVertex, indexBuffer, firstIndex);
 }
 
 void DrawVerticesOp::drawNonVolatile(Target* target) {
@@ -325,9 +328,10 @@
 
     bool hasColorAttribute;
     bool hasLocalCoordsAttribute;
-    sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
-                                                 &hasColorAttribute,
-                                                 &hasLocalCoordsAttribute);
+    GrGeometryProcessor* gp = this->makeGP(target->allocator(),
+                                           target->caps().shaderCaps(),
+                                            &hasColorAttribute,
+                                            &hasLocalCoordsAttribute);
 
     SkASSERT(fMeshes.count() == 1); // Non-volatile meshes should never combine.
 
@@ -351,8 +355,7 @@
 
     // Draw using the cached buffers if possible.
     if (vertexBuffer && (!this->isIndexed() || indexBuffer)) {
-        this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0,
-                           std::move(indexBuffer), 0);
+        this->drawVertices(target, gp, std::move(vertexBuffer), 0, std::move(indexBuffer), 0);
         return;
     }
 
@@ -396,8 +399,7 @@
     rp->assignUniqueKeyToResource(indexKey, indexBuffer.get());
 
     // Draw the vertices.
-    this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0, std::move(indexBuffer),
-                       0);
+    this->drawVertices(target, gp, std::move(vertexBuffer), 0, std::move(indexBuffer), 0);
 }
 
 void DrawVerticesOp::fillBuffers(bool hasColorAttribute,
@@ -493,7 +495,7 @@
 }
 
 void DrawVerticesOp::drawVertices(Target* target,
-                                  sk_sp<const GrGeometryProcessor> gp,
+                                  const GrGeometryProcessor* gp,
                                   sk_sp<const GrBuffer> vertexBuffer,
                                   int firstVertex,
                                   sk_sp<const GrBuffer> indexBuffer,
@@ -506,7 +508,7 @@
         mesh->setNonIndexedNonInstanced(fVertexCount);
     }
     mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-    target->recordDraw(std::move(gp), mesh);
+    target->recordDraw(gp, mesh, 1, this->primitiveType());
 }
 
 void DrawVerticesOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
diff --git a/src/gpu/ops/GrFillRRectOp.cpp b/src/gpu/ops/GrFillRRectOp.cpp
index 6b3c023..e0d3167 100644
--- a/src/gpu/ops/GrFillRRectOp.cpp
+++ b/src/gpu/ops/GrFillRRectOp.cpp
@@ -47,7 +47,8 @@
         }
     } else {
         if (GrAAType::kMSAA == aaType) {
-            if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleVariablesSupport()) {
+            if (!caps.sampleLocationsSupport() || !caps.shaderCaps()->sampleMaskSupport() ||
+                caps.shaderCaps()->canOnlyUseSampleMaskWithStencil()) {
                 return nullptr;
             }
         }
@@ -93,9 +94,9 @@
     return pool->allocate<GrFillRRectOp>(aaType, rrect, flags, m, std::move(paint), devBounds);
 }
 
-GrFillRRectOp::GrFillRRectOp(
-        GrAAType aaType, const SkRRect& rrect, Flags flags,
-        const SkMatrix& totalShapeMatrix, GrPaint&& paint, const SkRect& devBounds)
+GrFillRRectOp::GrFillRRectOp(GrAAType aaType, const SkRRect& rrect, Flags flags,
+                             const SkMatrix& totalShapeMatrix, GrPaint&& paint,
+                             const SkRect& devBounds)
         : GrDrawOp(ClassID())
         , fAAType(aaType)
         , fOriginalColor(paint.getColor4f())
@@ -134,7 +135,6 @@
 
     SkPMColor4f overrideColor;
     const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
-
             fOriginalColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
             &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType,
             &overrideColor);
@@ -174,8 +174,23 @@
 
 class GrFillRRectOp::Processor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, GrAAType aaType, Flags flags) {
+        return arena->make<Processor>(aaType, flags);
+    }
+
+    const char* name() const final { return "GrFillRRectOp::Processor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const final {
+        b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     Processor(GrAAType aaType, Flags flags)
-            : GrGeometryProcessor(kGrFillRRectOp_Processor_ClassID)
+            : INHERITED(kGrFillRRectOp_Processor_ClassID)
             , fAAType(aaType)
             , fFlags(flags) {
         int numVertexAttribs = (GrAAType::kCoverage == fAAType) ? 3 : 2;
@@ -207,15 +222,6 @@
         }
     }
 
-    const char* name() const override { return "GrFillRRectOp::Processor"; }
-
-    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        b->add32(((uint32_t)fFlags << 16) | (uint32_t)fAAType);
-    }
-
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override;
-
-private:
     static constexpr Attribute kVertexAttribs[] = {
             {"radii_selector", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
             {"corner_and_radius_outsets", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
@@ -230,6 +236,8 @@
 
     class CoverageImpl;
     class MSAAImpl;
+
+    typedef GrGeometryProcessor INHERITED;
 };
 
 constexpr GrPrimitiveProcessor::Attribute GrFillRRectOp::Processor::kVertexAttribs[];
@@ -441,6 +449,20 @@
 
 GR_DECLARE_STATIC_UNIQUE_KEY(gMSAAIndexBufferKey);
 
+void GrFillRRectOp::onPrePrepare(GrRecordingContext* context,
+                                 const GrSurfaceProxyView* dstView,
+                                 GrAppliedClip* clip,
+                                 const GrXferProcessor::DstProxyView& dstProxyView) {
+    SkArenaAlloc* arena = context->priv().recordTimeAllocator();
+
+    // This is equivalent to a GrOpFlushState::detachAppliedClip
+    GrAppliedClip appliedClip = clip ? std::move(*clip) : GrAppliedClip();
+
+    // TODO: need to also give this to the recording context
+    fProgramInfo = this->createProgramInfo(context->priv().caps(), arena, dstView,
+                                           std::move(appliedClip), dstProxyView);
+}
+
 void GrFillRRectOp::onPrepare(GrOpFlushState* flushState) {
     if (void* instanceData = flushState->makeVertexSpace(fInstanceStride, fInstanceCount,
                                                          &fInstanceBuffer, &fBaseInstance)) {
@@ -610,8 +632,8 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) override {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                 const CoordTransformRange& transformRange) override {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
     }
 };
 
@@ -716,8 +738,8 @@
     }
 
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) override {
-        this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                 const CoordTransformRange& transformRange) override {
+        this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
     }
 };
 
@@ -729,42 +751,67 @@
     return new CoverageImpl();
 }
 
-void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
-    if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
-        return;  // Setup failed.
-    }
-
-    Processor* proc = flushState->allocator()->make<Processor>(fAAType, fFlags);
-    SkASSERT(proc->instanceStride() == (size_t)fInstanceStride);
+GrProgramInfo* GrFillRRectOp::createProgramInfo(const GrCaps* caps,
+                                                SkArenaAlloc* arena,
+                                                const GrSurfaceProxyView* dstView,
+                                                GrAppliedClip&& appliedClip,
+                                                const GrXferProcessor::DstProxyView& dstProxyView) {
+    GrGeometryProcessor* geomProc = Processor::Make(arena, fAAType, fFlags);
+    SkASSERT(geomProc->instanceStride() == (size_t)fInstanceStride);
 
     GrPipeline::InitArgs initArgs;
     if (GrAAType::kMSAA == fAAType) {
         initArgs.fInputFlags = GrPipeline::InputFlags::kHWAntialias;
     }
-    initArgs.fCaps = &flushState->caps();
-    initArgs.fDstProxy = flushState->drawOpArgs().dstProxy();
-    initArgs.fOutputSwizzle = flushState->drawOpArgs().outputSwizzle();
-    auto clip = flushState->detachAppliedClip();
-    GrPipeline::FixedDynamicState* fixedDynamicState =
-        flushState->allocator()->make<GrPipeline::FixedDynamicState>(clip.scissorState().rect());
-    GrPipeline* pipeline = flushState->allocator()->make<GrPipeline>(initArgs,
-                                                                     std::move(fProcessors),
-                                                                     std::move(clip));
+    initArgs.fCaps = caps;
+    initArgs.fDstProxyView = dstProxyView;
+    initArgs.fOutputSwizzle = dstView->swizzle();
 
-    GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                              flushState->drawOpArgs().origin(),
-                              *pipeline,
-                              *proc,
-                              fixedDynamicState,
-                              nullptr, 0);
+    GrPipeline::FixedDynamicState* fixedDynamicState = nullptr;
+
+    if (appliedClip.scissorState().enabled()) {
+        fixedDynamicState = arena->make<GrPipeline::FixedDynamicState>(
+                                                        appliedClip.scissorState().rect());
+    }
+
+    GrPipeline* pipeline = arena->make<GrPipeline>(initArgs,
+                                                   std::move(fProcessors),
+                                                   std::move(appliedClip));
+
+    GrRenderTargetProxy* dstProxy = dstView->asRenderTargetProxy();
+    return arena->make<GrProgramInfo>(dstProxy->numSamples(),
+                                      dstProxy->numStencilSamples(),
+                                      dstProxy->backendFormat(),
+                                      dstView->origin(),
+                                      pipeline,
+                                      geomProc,
+                                      fixedDynamicState,
+                                      nullptr, 0,
+                                      GrPrimitiveType::kTriangles);
+}
+
+void GrFillRRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    if (!fInstanceBuffer || !fIndexBuffer || !fVertexBuffer) {
+        return;  // Setup failed.
+    }
+
+    if (!fProgramInfo) {
+        const GrSurfaceProxyView* dstView = flushState->view();
+
+        fProgramInfo = this->createProgramInfo(&flushState->caps(),
+                                               flushState->allocator(),
+                                               dstView,
+                                               flushState->detachAppliedClip(),
+                                               flushState->dstProxyView());
+    }
 
     GrMesh* mesh = flushState->allocator()->make<GrMesh>(GrPrimitiveType::kTriangles);
-    mesh->setIndexedInstanced(
-            std::move(fIndexBuffer), fIndexCount, std::move(fInstanceBuffer), fInstanceCount,
-            fBaseInstance, GrPrimitiveRestart::kNo);
+    mesh->setIndexedInstanced(std::move(fIndexBuffer), fIndexCount,
+                              std::move(fInstanceBuffer), fInstanceCount,
+                              fBaseInstance, GrPrimitiveRestart::kNo);
     mesh->setVertexData(std::move(fVertexBuffer));
-    flushState->opsRenderPass()->draw(programInfo, mesh, 1, this->bounds());
-    fIndexCount = 0;
+
+    flushState->opsRenderPass()->draw(*fProgramInfo, mesh, 1, this->bounds());
 }
 
 // Will the given corner look good if we use HW derivatives?
diff --git a/src/gpu/ops/GrFillRRectOp.h b/src/gpu/ops/GrFillRRectOp.h
index c3e7599..c6ad1e2 100644
--- a/src/gpu/ops/GrFillRRectOp.h
+++ b/src/gpu/ops/GrFillRRectOp.h
@@ -8,6 +8,7 @@
 #ifndef GrFillRRectOp_DEFINED
 #define GrFillRRectOp_DEFINED
 
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/ops/GrDrawOp.h"
 
 class GrRecordingContext;
@@ -20,21 +21,29 @@
             GrRecordingContext*, GrAAType, const SkMatrix& viewMatrix, const SkRRect&,
             const GrCaps&, GrPaint&&);
 
-    const char* name() const override { return "GrFillRRectOp"; }
-    FixedFunctionFlags fixedFunctionFlags() const override {
-        return (GrAAType::kMSAA == fAAType)
-                ? FixedFunctionFlags::kUsesHWAA
-                : FixedFunctionFlags::kNone;
+    const char* name() const final { return "GrFillRRectOp"; }
+
+    FixedFunctionFlags fixedFunctionFlags() const final {
+        return (GrAAType::kMSAA == fAAType) ? FixedFunctionFlags::kUsesHWAA
+                                            : FixedFunctionFlags::kNone;
     }
     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
-                                      bool hasMixedSampledCoverage, GrClampType) override;
-    CombineResult onCombineIfPossible(GrOp*, const GrCaps&) override;
+                                      bool hasMixedSampledCoverage, GrClampType) final;
+    CombineResult onCombineIfPossible(GrOp*, const GrCaps&) final;
     void visitProxies(const VisitProxyFunc& fn) const override {
-        fProcessors.visitProxies(fn);
+        if (fProgramInfo) {
+            fProgramInfo->visitProxies(fn);
+        } else {
+            fProcessors.visitProxies(fn);
+        }
     }
-    void onPrepare(GrOpFlushState*) override;
 
-    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
+    void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView*, GrAppliedClip*,
+                      const GrXferProcessor::DstProxyView&) final;
+
+    void onPrepare(GrOpFlushState*) final;
+
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) final;
 
 private:
     enum class Flags {
@@ -68,6 +77,13 @@
 
     void writeInstanceData() {}  // Halt condition.
 
+    // Create a GrProgramInfo object in the provided arena
+    GrProgramInfo* createProgramInfo(const GrCaps*,
+                                     SkArenaAlloc*,
+                                     const GrSurfaceProxyView* dstView,
+                                     GrAppliedClip&&,
+                                     const GrXferProcessor::DstProxyView&);
+
     const GrAAType fAAType;
     const SkPMColor4f fOriginalColor;
     const SkRect fLocalRect;
@@ -81,9 +97,13 @@
     sk_sp<const GrBuffer> fInstanceBuffer;
     sk_sp<const GrBuffer> fVertexBuffer;
     sk_sp<const GrBuffer> fIndexBuffer;
-    int fBaseInstance;
+    int fBaseInstance = 0;
     int fIndexCount = 0;
 
+    // If this op is prePrepared the created programInfo will be stored here from use in
+    // onExecute. In the prePrepared case it will have been stored in the record-time arena.
+    GrProgramInfo* fProgramInfo = nullptr;
+
     friend class GrOpMemoryPool;
 };
 
diff --git a/src/gpu/ops/GrFillRectOp.cpp b/src/gpu/ops/GrFillRectOp.cpp
index 560adc0..050a461 100644
--- a/src/gpu/ops/GrFillRectOp.cpp
+++ b/src/gpu/ops/GrFillRectOp.cpp
@@ -29,9 +29,10 @@
 using ColorType = GrQuadPerEdgeAA::ColorType;
 
 #ifdef SK_DEBUG
-static SkString dump_quad_info(int index, const GrQuad& deviceQuad,
-                               const GrQuad& localQuad, const SkPMColor4f& color,
+static SkString dump_quad_info(int index, const GrQuad* deviceQuad,
+                               const GrQuad* localQuad, const SkPMColor4f& color,
                                GrQuadAAFlags aaFlags) {
+    GrQuad safeLocal = localQuad ? *localQuad : GrQuad();
     SkString str;
     str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
                 "  device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
@@ -43,14 +44,14 @@
                 (uint32_t) (aaFlags & GrQuadAAFlags::kTop),
                 (uint32_t) (aaFlags & GrQuadAAFlags::kRight),
                 (uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
-                deviceQuad.x(0), deviceQuad.y(0), deviceQuad.w(0),
-                deviceQuad.x(1), deviceQuad.y(1), deviceQuad.w(1),
-                deviceQuad.x(2), deviceQuad.y(2), deviceQuad.w(2),
-                deviceQuad.x(3), deviceQuad.y(3), deviceQuad.w(3),
-                localQuad.x(0), localQuad.y(0), localQuad.w(0),
-                localQuad.x(1), localQuad.y(1), localQuad.w(1),
-                localQuad.x(2), localQuad.y(2), localQuad.w(2),
-                localQuad.x(3), localQuad.y(3), localQuad.w(3));
+                deviceQuad->x(0), deviceQuad->y(0), deviceQuad->w(0),
+                deviceQuad->x(1), deviceQuad->y(1), deviceQuad->w(1),
+                deviceQuad->x(2), deviceQuad->y(2), deviceQuad->w(2),
+                deviceQuad->x(3), deviceQuad->y(3), deviceQuad->w(3),
+                safeLocal.x(0), safeLocal.y(0), safeLocal.w(0),
+                safeLocal.x(1), safeLocal.y(1), safeLocal.w(1),
+                safeLocal.x(2), safeLocal.y(2), safeLocal.w(2),
+                safeLocal.x(3), safeLocal.y(3), safeLocal.w(3));
     return str;
 }
 #endif
@@ -178,13 +179,11 @@
     DEFINE_OP_CLASS_ID
 
 private:
-    // For GrFillRectOp::MakeSet's use of addQuad
-    friend std::unique_ptr<GrDrawOp> GrFillRectOp::MakeSet(
-            GrRecordingContext*,
-            GrPaint&&,
-            GrAAType, const SkMatrix& viewMatrix,
-            const GrRenderTargetContext::QuadSetEntry quads[], int quadCount,
-            const GrUserStencilSettings*);
+    friend class ::GrFillRectOp; // for access to addQuad
+
+#if GR_TEST_UTILS
+    int numQuads() const final { return fQuads.count(); }
+#endif
 
     void onPrepareDraws(Target* target) override {
         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
@@ -192,48 +191,58 @@
         using Domain = GrQuadPerEdgeAA::Domain;
         static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
 
+        auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(fHelper.aaType(),
+                                                                        fQuads.count());
+
         VertexSpec vertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
                               fHelper.usesLocalCoords(), Domain::kNo, fHelper.aaType(),
-                              fHelper.compatibleWithCoverageAsAlpha());
+                              fHelper.compatibleWithCoverageAsAlpha(), indexBufferOption);
         // Make sure that if the op thought it was a solid color, the vertex spec does not use
         // local coords.
         SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
 
-        sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeProcessor(vertexSpec);
+        GrGeometryProcessor* gp = GrQuadPerEdgeAA::MakeProcessor(target->allocator(), vertexSpec);
         size_t vertexSize = gp->vertexStride();
 
-        sk_sp<const GrBuffer> vbuffer;
+        sk_sp<const GrBuffer> vertexBuffer;
         int vertexOffsetInBuffer = 0;
 
+        const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
+
         // Fill the allocated vertex data
-        void* vdata = target->makeVertexSpace(
-                vertexSize, fQuads.count() * vertexSpec.verticesPerQuad(),
-                &vbuffer, &vertexOffsetInBuffer);
+        void* vdata = target->makeVertexSpace(vertexSize, totalNumVertices,
+                                              &vertexBuffer, &vertexOffsetInBuffer);
         if (!vdata) {
             SkDebugf("Could not allocate vertices\n");
             return;
         }
 
-        // vertices pointer advances through vdata based on Tessellate's return value
-        void* vertices = vdata;
+        GrQuadPerEdgeAA::Tessellator tessellator(vertexSpec, (char*) vdata);
         auto iter = fQuads.iterator();
         while(iter.next()) {
             // All entries should have local coords, or no entries should have local coords,
             // matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
             SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
             auto info = iter.metadata();
-            vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, iter.deviceQuad(),
-                    info.fColor, iter.localQuad(), kEmptyDomain, info.fAAFlags);
+            tessellator.append(iter.deviceQuad(), iter.localQuad(),
+                               info.fColor, kEmptyDomain, info.fAAFlags);
+        }
+
+        sk_sp<const GrBuffer> indexBuffer;
+        if (vertexSpec.needsIndexBuffer()) {
+            indexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target, vertexSpec.indexBufferOption());
+            if (!indexBuffer) {
+                SkDebugf("Could not allocate indices\n");
+                return;
+            }
         }
 
         // Configure the mesh for the vertex data
         GrMesh* mesh = target->allocMeshes(1);
-        if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, fQuads.count())) {
-            SkDebugf("Could not allocate indices\n");
-            return;
-        }
-        mesh->setVertexData(std::move(vbuffer), vertexOffsetInBuffer);
-        target->recordDraw(std::move(gp), mesh);
+        GrQuadPerEdgeAA::ConfigureMesh(target->caps(), mesh, vertexSpec, 0, fQuads.count(),
+                                       totalNumVertices, std::move(vertexBuffer),
+                                       std::move(indexBuffer), vertexOffsetInBuffer);
+        target->recordDraw(gp, mesh, 1, vertexSpec.primitiveType());
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -244,10 +253,16 @@
         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
         const auto* that = t->cast<FillRectOp>();
 
-        if ((fHelper.aaType() == GrAAType::kCoverage ||
-             that->fHelper.aaType() == GrAAType::kCoverage) &&
-            fQuads.count() + that->fQuads.count() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) {
-            // This limit on batch size seems to help on Adreno devices
+        bool upgradeToCoverageAAOnMerge = false;
+        if (fHelper.aaType() != that->fHelper.aaType()) {
+            if (!CanUpgradeAAOnMerge(fHelper.aaType(), that->fHelper.aaType())) {
+                return CombineResult::kCannotCombine;
+            }
+            upgradeToCoverageAAOnMerge = true;
+        }
+
+        if (CombinedQuadCountWillOverflow(fHelper.aaType(), upgradeToCoverageAAOnMerge,
+                                          fQuads.count() + that->fQuads.count())) {
             return CombineResult::kCannotCombine;
         }
 
@@ -268,7 +283,7 @@
         // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa
         // types to be none and coverage, in which case this op's aa type must be lifted to coverage
         // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed.
-        if (fHelper.aaType() == GrAAType::kNone && that->fHelper.aaType() == GrAAType::kCoverage) {
+        if (upgradeToCoverageAAOnMerge) {
             fHelper.setAAType(GrAAType::kCoverage);
         }
 
@@ -280,19 +295,29 @@
     // But since it's avoiding the op list management, it must update the op's bounds. This is only
     // used with quad sets, which uses the same view matrix for each quad so this assumes that the
     // device quad type of the new quad is the same as the op's.
-    void addQuad(const GrQuad& deviceQuad, const GrQuad& localQuad,
+    bool addQuad(const GrQuad& deviceQuad, const GrQuad& localQuad,
                  const SkPMColor4f& color, GrQuadAAFlags edgeAA, GrAAType aaType) {
         // The new quad's aa type should be the same as the first quad's or none, except when the
         // first quad's aa type was already downgraded to none, in which case the stored type must
         // be lifted to back to the requested type.
-        if (aaType != fHelper.aaType()) {
-            if (aaType != GrAAType::kNone) {
-                // Original quad was downgraded to non-aa, lift back up to this quad's required type
-                SkASSERT(fHelper.aaType() == GrAAType::kNone);
-                fHelper.setAAType(aaType);
+        if (aaType != fHelper.aaType() && aaType != GrAAType::kNone) {
+            auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(aaType,
+                                                                            fQuads.count()+1);
+            if (fQuads.count()+1 > GrQuadPerEdgeAA::QuadLimit(indexBufferOption)) {
+                // Promoting to the new aaType would've caused an overflow of the indexBuffer
+                // limit
+                return false;
             }
-            // else the new quad could have been downgraded but the other quads can't be, so don't
-            // reset the op's accumulated aa type.
+
+            // Original quad was downgraded to non-aa, lift back up to this quad's required type
+            SkASSERT(fHelper.aaType() == GrAAType::kNone);
+            fHelper.setAAType(aaType);
+        } else {
+            auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(fHelper.aaType(),
+                                                                            fQuads.count()+1);
+            if (fQuads.count()+1 > GrQuadPerEdgeAA::QuadLimit(indexBufferOption)) {
+                return false; // This op can't grow any more
+            }
         }
 
         // Update the bounds and add the quad to this op's storage
@@ -301,6 +326,7 @@
         this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
                         IsHairline::kNo);
         fQuads.append(deviceQuad, { color, edgeAA }, fHelper.isTrivial() ? nullptr : &localQuad);
+        return true;
     }
 
     struct ColorAndAA {
@@ -318,45 +344,46 @@
 
 } // anonymous namespace
 
-namespace GrFillRectOp {
-
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                               GrPaint&& paint,
-                               GrAAType aaType,
-                               GrQuadAAFlags aaFlags,
-                               const GrQuad& deviceQuad,
-                               const GrQuad& localQuad,
-                               const GrUserStencilSettings* stencil) {
+std::unique_ptr<GrDrawOp> GrFillRectOp::Make(GrRecordingContext* context,
+                                             GrPaint&& paint,
+                                             GrAAType aaType,
+                                             GrQuadAAFlags aaFlags,
+                                             const GrQuad& deviceQuad,
+                                             const GrQuad& localQuad,
+                                             const GrUserStencilSettings* stencil) {
     return FillRectOp::Make(context, std::move(paint), aaType, aaFlags, stencil,
                             deviceQuad, localQuad);
 }
 
-std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
-                                        GrPaint&& paint,
-                                        const SkMatrix& view,
-                                        const SkRect& rect,
-                                        const GrUserStencilSettings* stencil) {
+std::unique_ptr<GrDrawOp> GrFillRectOp::MakeNonAARect(GrRecordingContext* context,
+                                                      GrPaint&& paint,
+                                                      const SkMatrix& view,
+                                                      const SkRect& rect,
+                                                      const GrUserStencilSettings* stencil) {
     return FillRectOp::Make(context, std::move(paint), GrAAType::kNone, GrQuadAAFlags::kNone,
                             stencil, GrQuad::MakeFromRect(rect, view), GrQuad(rect));
 }
 
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
-                                  GrPaint&& paint,
-                                  GrAAType aaType,
-                                  const SkMatrix& viewMatrix,
-                                  const GrRenderTargetContext::QuadSetEntry quads[],
-                                  int cnt,
-                                  const GrUserStencilSettings* stencilSettings) {
+std::unique_ptr<GrDrawOp> GrFillRectOp::MakeOp(GrRecordingContext* context,
+                                               GrPaint&& paint,
+                                               GrAAType aaType,
+                                               const SkMatrix& viewMatrix,
+                                               const GrRenderTargetContext::QuadSetEntry quads[],
+                                               int cnt,
+                                               const GrUserStencilSettings* stencilSettings,
+                                               int* numConsumed) {
     // First make a draw op for the first quad in the set
     SkASSERT(cnt > 0);
 
     paint.setColor4f(quads[0].fColor);
-    std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType,
+    std::unique_ptr<GrDrawOp> op = FillRectOp::Make(
+            context, std::move(paint), aaType,
             quads[0].fAAFlags, stencilSettings,
             GrQuad::MakeFromRect(quads[0].fRect, viewMatrix),
             GrQuad::MakeFromRect(quads[0].fRect, quads[0].fLocalMatrix));
-    auto* fillRects = op->cast<FillRectOp>();
+    FillRectOp* fillRects = op->cast<FillRectOp>();
 
+    *numConsumed = 1;
     // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
     for (int i = 1; i < cnt; ++i) {
         GrQuad deviceQuad = GrQuad::MakeFromRect(quads[i].fRect, viewMatrix);
@@ -366,18 +393,52 @@
         GrQuadUtils::ResolveAAType(aaType, quads[i].fAAFlags, deviceQuad,
                                    &resolvedAA, &resolvedEdgeFlags);
 
-        fillRects->addQuad(deviceQuad,
-                           GrQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
-                           quads[i].fColor, resolvedEdgeFlags,resolvedAA);
+        if (!fillRects->addQuad(deviceQuad,
+                                GrQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
+                                quads[i].fColor, resolvedEdgeFlags, resolvedAA)) {
+            break;
+        }
+
+        (*numConsumed)++;
     }
 
     return op;
 }
 
-} // namespace GrFillRectOp
+void GrFillRectOp::AddFillRectOps(GrRenderTargetContext* rtc,
+                                  const GrClip& clip,
+                                  GrRecordingContext* context,
+                                  GrPaint&& paint,
+                                  GrAAType aaType,
+                                  const SkMatrix& viewMatrix,
+                                  const GrRenderTargetContext::QuadSetEntry quads[],
+                                  int cnt,
+                                  const GrUserStencilSettings* stencilSettings) {
+
+    int offset = 0;
+    int numLeft = cnt;
+    while (numLeft) {
+        int numConsumed = 0;
+
+        std::unique_ptr<GrDrawOp> op = MakeOp(context, GrPaint::Clone(paint), aaType, viewMatrix,
+                                              &quads[offset], numLeft, stencilSettings,
+                                              &numConsumed);
+
+        offset += numConsumed;
+        numLeft -= numConsumed;
+
+        rtc->addDrawOp(clip, std::move(op));
+    }
+
+    SkASSERT(offset == cnt);
+}
 
 #if GR_TEST_UTILS
 
+uint32_t GrFillRectOp::ClassID() {
+    return FillRectOp::ClassID();
+}
+
 #include "src/gpu/GrDrawOpTest.h"
 #include "src/gpu/SkGr.h"
 
@@ -400,35 +461,11 @@
 
     if (random->nextBool()) {
         if (random->nextBool()) {
-            if (random->nextBool()) {
-                // Local matrix with a set op
-                uint32_t extraQuadCt = random->nextRangeU(1, 4);
-                SkTArray<GrRenderTargetContext::QuadSetEntry> quads(extraQuadCt + 1);
-                quads.push_back(
-                        {rect, SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
-                         GrTest::TestMatrixInvertible(random), aaFlags});
-                for (uint32_t i = 0; i < extraQuadCt; ++i) {
-                    GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
-                    aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
-                    aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
-                    aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
-                    aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
-
-                    quads.push_back(
-                        {GrTest::TestRect(random),
-                         SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())),
-                         GrTest::TestMatrixInvertible(random), aaFlags});
-                }
-
-                return GrFillRectOp::MakeSet(context, std::move(paint), aaType, viewMatrix,
-                                             quads.begin(), quads.count(), stencil);
-            } else {
-                // Single local matrix
-                SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
-                return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
-                                          GrQuad::MakeFromRect(rect, viewMatrix),
-                                          GrQuad::MakeFromRect(rect, localMatrix), stencil);
-            }
+            // Single local matrix
+            SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
+            return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
+                                      GrQuad::MakeFromRect(rect, viewMatrix),
+                                      GrQuad::MakeFromRect(rect, localMatrix), stencil);
         } else {
             // Pass local rect directly
             SkRect localRect = GrTest::TestRect(random);
diff --git a/src/gpu/ops/GrFillRectOp.h b/src/gpu/ops/GrFillRectOp.h
index 768ed8b..600e87b 100644
--- a/src/gpu/ops/GrFillRectOp.h
+++ b/src/gpu/ops/GrFillRectOp.h
@@ -25,34 +25,53 @@
  * the GrPaint is only consumed by these methods if a valid op is returned. If null is returned then
  * the paint is unmodified and may still be used.
  */
-namespace GrFillRectOp {
+class GrFillRectOp {
+public:
 
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                               GrPaint&& paint,
-                               GrAAType aaType,
-                               GrQuadAAFlags aaFlags,
-                               const GrQuad& deviceQuad,
-                               const GrQuad& localQuad,
-                               const GrUserStencilSettings* stencil = nullptr);
+    static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
+                                          GrPaint&& paint,
+                                          GrAAType aaType,
+                                          GrQuadAAFlags aaFlags,
+                                          const GrQuad& deviceQuad,
+                                          const GrQuad& localQuad,
+                                          const GrUserStencilSettings* stencil = nullptr);
 
-// Utility function to create a non-AA rect transformed by view. This is used commonly enough in
-// testing and GMs that manage ops without going through GrRTC that it's worth the convenience.
-std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
-                                        GrPaint&& paint,
-                                        const SkMatrix& view,
-                                        const SkRect& rect,
-                                        const GrUserStencilSettings* stencil = nullptr);
+    // Utility function to create a non-AA rect transformed by view. This is used commonly enough
+    // in testing and GMs that manage ops without going through GrRTC that it's worth the
+    // convenience.
+    static std::unique_ptr<GrDrawOp> MakeNonAARect(GrRecordingContext* context,
+                                                   GrPaint&& paint,
+                                                   const SkMatrix& view,
+                                                   const SkRect& rect,
+                                                   const GrUserStencilSettings* stencil = nullptr);
 
-// Bulk API for drawing quads with a single op
-// TODO(michaelludwig) - remove if the bulk API is not useful for SkiaRenderer
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
-                                  GrPaint&& paint,
-                                  GrAAType aaType,
-                                  const SkMatrix& viewMatrix,
-                                  const GrRenderTargetContext::QuadSetEntry quads[],
-                                  int quadCount,
-                                  const GrUserStencilSettings* stencil = nullptr);
+    // Bulk API for drawing quads with a single op
+    // TODO(michaelludwig) - remove if the bulk API is not useful for SkiaRenderer
+    static void AddFillRectOps(GrRenderTargetContext*,
+                               const GrClip& clip,
+                               GrRecordingContext*,
+                               GrPaint&&,
+                               GrAAType,
+                               const SkMatrix& viewMatrix,
+                               const GrRenderTargetContext::QuadSetEntry quads[],
+                               int quadCount,
+                               const GrUserStencilSettings* = nullptr);
 
-} // namespace GrFillRectOp
+#if GR_TEST_UTILS
+    static uint32_t ClassID();
+#endif
+
+private:
+    // Create a GrFillRectOp that uses as many quads as possible from 'quads' w/o exceeding
+    // any index buffer size limits.
+    static std::unique_ptr<GrDrawOp> MakeOp(GrRecordingContext*,
+                                            GrPaint&&,
+                                            GrAAType,
+                                            const SkMatrix& viewMatrix,
+                                            const GrRenderTargetContext::QuadSetEntry quads[],
+                                            int quadCount,
+                                            const GrUserStencilSettings*,
+                                            int* numConsumed);
+};
 
 #endif // GrFillRectOp_DEFINED
diff --git a/src/gpu/ops/GrLatticeOp.cpp b/src/gpu/ops/GrLatticeOp.cpp
index d63c7d6..b9c11ff 100644
--- a/src/gpu/ops/GrLatticeOp.cpp
+++ b/src/gpu/ops/GrLatticeOp.cpp
@@ -18,6 +18,7 @@
 #include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
 #include "src/gpu/glsl/GrGLSLVarying.h"
 #include "src/gpu/ops/GrLatticeOp.h"
@@ -28,13 +29,12 @@
 
 class LatticeGP : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(GrGpu* gpu,
-                                           const GrTextureProxy* proxy,
-                                           sk_sp<GrColorSpaceXform> csxf,
-                                           GrSamplerState::Filter filter,
-                                           bool wideColor) {
-        return sk_sp<GrGeometryProcessor>(
-                new LatticeGP(gpu, proxy, std::move(csxf), filter, wideColor));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena,
+                                     const GrTextureProxy* proxy,
+                                     sk_sp<GrColorSpaceXform> csxf,
+                                     GrSamplerState::Filter filter,
+                                     bool wideColor) {
+        return arena->make<LatticeGP>(proxy, std::move(csxf), filter, wideColor);
     }
 
     const char* name() const override { return "LatticeGP"; }
@@ -47,9 +47,9 @@
         class GLSLProcessor : public GrGLSLGeometryProcessor {
         public:
             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
-                         FPCoordTransformIter&& transformIter) override {
+                         const CoordTransformRange& transformRange) override {
                 const auto& latticeGP = proc.cast<LatticeGP>();
-                this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
                 fColorSpaceXformHelper.setData(pdman, latticeGP.fColorSpaceXform.get());
             }
 
@@ -92,16 +92,15 @@
     }
 
 private:
-    LatticeGP(GrGpu* gpu, const GrTextureProxy* proxy, sk_sp<GrColorSpaceXform> csxf,
+    friend class ::SkArenaAlloc; // for access to ctor
+
+    LatticeGP(const GrTextureProxy* proxy, sk_sp<GrColorSpaceXform> csxf,
               GrSamplerState::Filter filter, bool wideColor)
-            : INHERITED(kLatticeGP_ClassID), fColorSpaceXform(std::move(csxf)) {
+            : INHERITED(kLatticeGP_ClassID)
+            , fColorSpaceXform(std::move(csxf)) {
 
-        GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp, filter);
-        uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(samplerState,
-                                                                     proxy->backendFormat());
-
-        fSampler.reset(samplerState, proxy->backendFormat(), proxy->textureSwizzle(),
-                       extraSamplerKey);
+        fSampler.reset(GrSamplerState(GrSamplerState::WrapMode::kClamp, filter),
+                       proxy->backendFormat(), proxy->textureSwizzle());
         this->setTextureSamplerCnt(1);
         fInPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
         fInTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
@@ -130,9 +129,6 @@
 public:
     DEFINE_OP_CLASS_ID
 
-    static const int kVertsPerRect = 4;
-    static const int kIndicesPerRect = 6;
-
     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
                                           GrPaint&& paint,
                                           const SkMatrix& viewMatrix,
@@ -213,8 +209,8 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        GrGpu* gpu = target->resourceProvider()->priv().gpu();
-        auto gp = LatticeGP::Make(gpu, fProxy.get(), fColorSpaceXform, fFilter, fWideColor);
+        auto gp = LatticeGP::Make(target->allocator(), fProxy.get(), fColorSpaceXform,
+                                  fFilter, fWideColor);
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
@@ -231,13 +227,9 @@
         }
 
         const size_t kVertexStride = gp->vertexStride();
-        sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-        if (!indexBuffer) {
-            SkDebugf("Could not allocate indices\n");
-            return;
-        }
-        PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride,
-                             std::move(indexBuffer), kVertsPerRect, kIndicesPerRect, numRects);
+
+        QuadHelper helper(target, kVertexStride, numRects);
+
         GrVertexWriter vertices{helper.vertices()};
         if (!vertices.fPtr) {
             SkDebugf("Could not allocate vertices\n");
@@ -287,13 +279,14 @@
 
             // If we didn't handle it above, apply the matrix here.
             if (!isScaleTranslate) {
-                SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, patchPositions, kVertexStride,
-                                                  kVertsPerRect * patch.fIter->numRectsToDraw());
+                SkMatrixPriv::MapPointsWithStride(
+                    patch.fViewMatrix, patchPositions, kVertexStride,
+                    GrResourceProvider::NumVertsPerNonAAQuad() * patch.fIter->numRectsToDraw());
             }
         }
         auto fixedDynamicState = target->makeFixedDynamicState(1);
         fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy.get();
-        helper.recordDraw(target, std::move(gp), fixedDynamicState);
+        helper.recordDraw(target, gp, fixedDynamicState);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
diff --git a/src/gpu/ops/GrMeshDrawOp.cpp b/src/gpu/ops/GrMeshDrawOp.cpp
index 77dcf6d..bfd052b 100644
--- a/src/gpu/ops/GrMeshDrawOp.cpp
+++ b/src/gpu/ops/GrMeshDrawOp.cpp
@@ -20,15 +20,15 @@
 GrMeshDrawOp::PatternHelper::PatternHelper(Target* target, GrPrimitiveType primitiveType,
                                            size_t vertexStride, sk_sp<const GrBuffer> indexBuffer,
                                            int verticesPerRepetition, int indicesPerRepetition,
-                                           int repeatCount) {
+                                           int repeatCount, int maxRepetitions) {
     this->init(target, primitiveType, vertexStride, std::move(indexBuffer), verticesPerRepetition,
-               indicesPerRepetition, repeatCount);
+               indicesPerRepetition, repeatCount, maxRepetitions);
 }
 
 void GrMeshDrawOp::PatternHelper::init(Target* target, GrPrimitiveType primitiveType,
                                        size_t vertexStride, sk_sp<const GrBuffer> indexBuffer,
                                        int verticesPerRepetition, int indicesPerRepetition,
-                                       int repeatCount) {
+                                       int repeatCount, int maxRepetitions) {
     SkASSERT(target);
     if (!indexBuffer) {
         return;
@@ -42,35 +42,38 @@
         return;
     }
     SkASSERT(vertexBuffer);
-    size_t ibSize = indexBuffer->size();
-    int maxRepetitions = static_cast<int>(ibSize / (sizeof(uint16_t) * indicesPerRepetition));
     fMesh = target->allocMesh(primitiveType);
+    fPrimitiveType = primitiveType;
+
+    SkASSERT(maxRepetitions ==
+             static_cast<int>(indexBuffer->size() / (sizeof(uint16_t) * indicesPerRepetition)));
     fMesh->setIndexedPatterned(std::move(indexBuffer), indicesPerRepetition, verticesPerRepetition,
                                repeatCount, maxRepetitions);
     fMesh->setVertexData(std::move(vertexBuffer), firstVertex);
 }
 
-void GrMeshDrawOp::PatternHelper::recordDraw(
-        Target* target, sk_sp<const GrGeometryProcessor> gp) const {
-    target->recordDraw(std::move(gp), fMesh);
+void GrMeshDrawOp::PatternHelper::recordDraw(Target* target, const GrGeometryProcessor* gp) const {
+    target->recordDraw(gp, fMesh, 1, fPrimitiveType);
 }
 
 void GrMeshDrawOp::PatternHelper::recordDraw(
-        Target* target, sk_sp<const GrGeometryProcessor> gp,
+        Target* target, const GrGeometryProcessor* gp,
         const GrPipeline::FixedDynamicState* fixedDynamicState) const {
-    target->recordDraw(std::move(gp), fMesh, 1, fixedDynamicState, nullptr);
+    target->recordDraw(gp, fMesh, 1, fixedDynamicState, nullptr, fPrimitiveType);
 }
 
 //////////////////////////////////////////////////////////////////////////////
 
 GrMeshDrawOp::QuadHelper::QuadHelper(Target* target, size_t vertexStride, int quadsToDraw) {
-    sk_sp<const GrGpuBuffer> quadIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-    if (!quadIndexBuffer) {
+    sk_sp<const GrGpuBuffer> indexBuffer = target->resourceProvider()->refNonAAQuadIndexBuffer();
+    if (!indexBuffer) {
         SkDebugf("Could not get quad index buffer.");
         return;
     }
-    this->init(target, GrPrimitiveType::kTriangles, vertexStride, std::move(quadIndexBuffer),
-               kVerticesPerQuad, kIndicesPerQuad, quadsToDraw);
+    this->init(target, GrPrimitiveType::kTriangles, vertexStride, std::move(indexBuffer),
+               GrResourceProvider::NumVertsPerNonAAQuad(),
+               GrResourceProvider::NumIndicesPerNonAAQuad(), quadsToDraw,
+               GrResourceProvider::MaxNumNonAAQuads());
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -86,7 +89,7 @@
     }
 
     if (numPrimitiveProcessorTextures) {
-        result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrTextureProxy*>(
+        result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrSurfaceProxy*>(
                         numPrimitiveProcessorTextures * numMeshes);
     }
 
@@ -96,13 +99,17 @@
 GrPipeline::FixedDynamicState* GrMeshDrawOp::Target::MakeFixedDynamicState(
         SkArenaAlloc* arena, const GrAppliedClip* clip, int numPrimProcTextures) {
 
-    if ((clip && clip->scissorState().enabled()) || numPrimProcTextures) {
-        const SkIRect& scissor = (clip) ? clip->scissorState().rect() : SkIRect::MakeEmpty();
+    bool haveScissor = clip && clip->scissorState().enabled();
 
-        auto result = arena->make<GrPipeline::FixedDynamicState>(scissor);
+    if (haveScissor || numPrimProcTextures) {
+        auto result = arena->make<GrPipeline::FixedDynamicState>();
+
+        if (haveScissor) {
+            result->fScissorRect = clip->scissorState().rect();
+        }
 
         if (numPrimProcTextures) {
-            result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrTextureProxy*>(
+            result->fPrimitiveProcessorTextures = arena->makeArrayDefault<GrSurfaceProxy*>(
                         numPrimProcTextures);
         }
         return result;
diff --git a/src/gpu/ops/GrMeshDrawOp.h b/src/gpu/ops/GrMeshDrawOp.h
index 91286d1..1922266 100644
--- a/src/gpu/ops/GrMeshDrawOp.h
+++ b/src/gpu/ops/GrMeshDrawOp.h
@@ -8,6 +8,7 @@
 #ifndef GrMeshDrawOp_DEFINED
 #define GrMeshDrawOp_DEFINED
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/gpu/GrAppliedClip.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrMesh.h"
@@ -27,6 +28,11 @@
     /** Abstract interface that represents a destination for a GrMeshDrawOp. */
     class Target;
 
+    static bool CanUpgradeAAOnMerge(GrAAType aa1, GrAAType aa2) {
+        return (aa1 == GrAAType::kNone && aa2 == GrAAType::kCoverage) ||
+               (aa1 == GrAAType::kCoverage && aa2 == GrAAType::kNone);
+    }
+
 protected:
     GrMeshDrawOp(uint32_t classID);
 
@@ -36,11 +42,11 @@
     public:
         PatternHelper(Target*, GrPrimitiveType, size_t vertexStride,
                       sk_sp<const GrBuffer> indexBuffer, int verticesPerRepetition,
-                      int indicesPerRepetition, int repeatCount);
+                      int indicesPerRepetition, int repeatCount, int maxRepetitions);
 
         /** Called to issue draws to the GrMeshDrawOp::Target.*/
-        void recordDraw(Target*, sk_sp<const GrGeometryProcessor>) const;
-        void recordDraw(Target*, sk_sp<const GrGeometryProcessor>,
+        void recordDraw(Target*, const GrGeometryProcessor*) const;
+        void recordDraw(Target*, const GrGeometryProcessor*,
                         const GrPipeline::FixedDynamicState*) const;
 
         void* vertices() const { return fVertices; }
@@ -48,17 +54,18 @@
     protected:
         PatternHelper() = default;
         void init(Target*, GrPrimitiveType, size_t vertexStride, sk_sp<const GrBuffer> indexBuffer,
-                  int verticesPerRepetition, int indicesPerRepetition, int repeatCount);
+                  int verticesPerRepetition, int indicesPerRepetition, int repeatCount,
+                  int maxRepetitions);
 
     private:
         void* fVertices = nullptr;
         GrMesh* fMesh = nullptr;
+        GrPrimitiveType fPrimitiveType;
     };
 
-    static const int kVerticesPerQuad = 4;
-    static const int kIndicesPerQuad = 6;
-
-    /** A specialization of InstanceHelper for quad rendering. */
+    /** A specialization of InstanceHelper for quad rendering.
+     *  It only draws non-antialiased indexed quads.
+     */
     class QuadHelper : private PatternHelper {
     public:
         QuadHelper() = delete;
@@ -71,14 +78,29 @@
         typedef PatternHelper INHERITED;
     };
 
+    static bool CombinedQuadCountWillOverflow(GrAAType aaType,
+                                              bool willBeUpgradedToAA,
+                                              int combinedQuadCount) {
+        bool willBeAA = (aaType == GrAAType::kCoverage) || willBeUpgradedToAA;
+
+        return combinedQuadCount > (willBeAA ? GrResourceProvider::MaxNumAAQuads()
+                                             : GrResourceProvider::MaxNumNonAAQuads());
+    }
+
 private:
-    void onPrePrepare(GrRecordingContext* context, const GrAppliedClip* clip) final {
-        this->onPrePrepareDraws(context, clip);
+    void onPrePrepare(GrRecordingContext* context,
+                      const GrSurfaceProxyView* dstView,
+                      GrAppliedClip* clip,
+                      const GrXferProcessor::DstProxyView& dstProxyView) final {
+        this->onPrePrepareDraws(context, dstView, clip, dstProxyView);
     }
     void onPrepare(GrOpFlushState* state) final;
 
     // Only the GrTextureOp currently overrides this virtual
-    virtual void onPrePrepareDraws(GrRecordingContext*, const GrAppliedClip*) {}
+    virtual void onPrePrepareDraws(GrRecordingContext*,
+                                   const GrSurfaceProxyView*,
+                                   GrAppliedClip*,
+                                   const GrXferProcessor::DstProxyView&) {}
 
     virtual void onPrepareDraws(Target*) = 0;
     typedef GrDrawOp INHERITED;
@@ -90,17 +112,19 @@
 
     /** Adds a draw of a mesh. */
     virtual void recordDraw(
-            sk_sp<const GrGeometryProcessor>, const GrMesh[], int meshCnt,
-            const GrPipeline::FixedDynamicState*, const GrPipeline::DynamicStateArrays*) = 0;
+            const GrGeometryProcessor*, const GrMesh[], int meshCnt,
+            const GrPipeline::FixedDynamicState*, const GrPipeline::DynamicStateArrays*,
+            GrPrimitiveType) = 0;
 
     /**
      * Helper for drawing GrMesh(es) with zero primProc textures and no dynamic state besides the
      * scissor clip.
      */
-    void recordDraw(sk_sp<const GrGeometryProcessor> gp, const GrMesh meshes[], int meshCnt = 1) {
+    void recordDraw(const GrGeometryProcessor* gp, const GrMesh meshes[], int meshCnt,
+                    GrPrimitiveType primitiveType) {
         static constexpr int kZeroPrimProcTextures = 0;
         auto fixedDynamicState = this->makeFixedDynamicState(kZeroPrimProcTextures);
-        this->recordDraw(std::move(gp), meshes, meshCnt, fixedDynamicState, nullptr);
+        this->recordDraw(gp, meshes, meshCnt, fixedDynamicState, nullptr, primitiveType);
     }
 
     /**
@@ -165,10 +189,10 @@
 
     virtual GrRenderTargetProxy* proxy() const = 0;
 
-    virtual const GrAppliedClip* appliedClip() = 0;
+    virtual const GrAppliedClip* appliedClip() const = 0;
     virtual GrAppliedClip detachAppliedClip() = 0;
 
-    virtual const GrXferProcessor::DstProxy& dstProxy() const = 0;
+    virtual const GrXferProcessor::DstProxyView& dstProxyView() const = 0;
 
     virtual GrResourceProvider* resourceProvider() const = 0;
     uint32_t contextUniqueID() const { return this->resourceProvider()->contextUniqueID(); }
@@ -179,7 +203,7 @@
     // This should be called during onPrepare of a GrOp. The caller should add any proxies to the
     // array it will use that it did not access during a call to visitProxies. This is usually the
     // case for atlases.
-    virtual SkTArray<GrTextureProxy*, true>* sampledProxyArray() = 0;
+    virtual SkTArray<GrSurfaceProxy*, true>* sampledProxyArray() = 0;
 
     virtual const GrCaps& caps() const = 0;
 
diff --git a/src/gpu/ops/GrOp.h b/src/gpu/ops/GrOp.h
index 9a250a3..2280f40 100644
--- a/src/gpu/ops/GrOp.h
+++ b/src/gpu/ops/GrOp.h
@@ -69,7 +69,7 @@
 
     virtual const char* name() const = 0;
 
-    using VisitProxyFunc = std::function<void(GrTextureProxy*, GrMipMapped)>;
+    using VisitProxyFunc = std::function<void(GrSurfaceProxy*, GrMipMapped)>;
 
     virtual void visitProxies(const VisitProxyFunc&) const {
         // This default implementation assumes the op has no proxies
@@ -159,8 +159,9 @@
      * onPrePrepare must be prepared to handle both cases (when onPrePrepare has been called
      * ahead of time and when it has not been called).
      */
-    void prePrepare(GrRecordingContext* context, GrAppliedClip* clip) {
-        this->onPrePrepare(context, clip);
+    void prePrepare(GrRecordingContext* context, GrSurfaceProxyView* dstView, GrAppliedClip* clip,
+                    const GrXferProcessor::DstProxyView& dstProxyView) {
+        this->onPrePrepare(context, dstView, clip, dstProxyView);
     }
 
     /**
@@ -291,8 +292,11 @@
         return CombineResult::kCannotCombine;
     }
 
-    // Only GrMeshDrawOp currently overrides this virtual
-    virtual void onPrePrepare(GrRecordingContext*, const GrAppliedClip*) {}
+    // TODO: the parameters to onPrePrepare mirror GrOpFlushState::OpArgs - fuse the two?
+    virtual void onPrePrepare(GrRecordingContext*,
+                              const GrSurfaceProxyView*,
+                              GrAppliedClip*,
+                              const GrXferProcessor::DstProxyView&) {}
     virtual void onPrepare(GrOpFlushState*) = 0;
     // If this op is chained then chainBounds is the union of the bounds of all ops in the chain.
     // Otherwise, this op's bounds.
diff --git a/src/gpu/ops/GrOvalOpFactory.cpp b/src/gpu/ops/GrOvalOpFactory.cpp
index b327581..6adb970 100644
--- a/src/gpu/ops/GrOvalOpFactory.cpp
+++ b/src/gpu/ops/GrOvalOpFactory.cpp
@@ -63,6 +63,26 @@
 
 class CircleGeometryProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool clipPlane,
+                                     bool isectPlane, bool unionPlane, bool roundCaps,
+                                     bool wideColor, const SkMatrix& localMatrix) {
+        return arena->make<CircleGeometryProcessor>(stroke, clipPlane, isectPlane, unionPlane,
+                                                    roundCaps, wideColor, localMatrix);
+    }
+
+    const char* name() const override { return "CircleGeometryProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GLSLProcessor::GenKey(*this, caps, b);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
+        return new GLSLProcessor();
+    }
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
                             bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
             : INHERITED(kCircleGeometryProcessor_ClassID)
@@ -90,19 +110,6 @@
         this->setVertexAttributes(&fInPosition, 7);
     }
 
-    ~CircleGeometryProcessor() override {}
-
-    const char* name() const override { return "CircleEdge"; }
-
-    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLProcessor::GenKey(*this, caps, b);
-    }
-
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
-        return new GLSLProcessor();
-    }
-
-private:
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
         GLSLProcessor() {}
@@ -214,9 +221,9 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
-                                         pdman, &transformIter);
+                                         pdman, transformRange);
         }
 
     private:
@@ -243,7 +250,7 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
     bool stroke = d->fRandom->nextBool();
     bool roundCaps = stroke ? d->fRandom->nextBool() : false;
     bool wideColor = d->fRandom->nextBool();
@@ -251,20 +258,16 @@
     bool isectPlane = d->fRandom->nextBool();
     bool unionPlane = d->fRandom->nextBool();
     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
-    return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
-            stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
+    return CircleGeometryProcessor::Make(d->allocator(), stroke, clipPlane, isectPlane,
+                                         unionPlane, roundCaps, wideColor, matrix);
 }
 #endif
 
 class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
 public:
-    ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
-            : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
-        fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
-        fInColor = MakeColorAttribute("inColor", wideColor);
-        fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
-        fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
-        this->setVertexAttributes(&fInPosition, 4);
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor,
+                                     const SkMatrix& localMatrix) {
+        return arena->make<ButtCapDashedCircleGeometryProcessor>(wideColor, localMatrix);
     }
 
     ~ButtCapDashedCircleGeometryProcessor() override {}
@@ -280,6 +283,18 @@
     }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
+    ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
+            : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID)
+            , fLocalMatrix(localMatrix) {
+        fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
+        fInColor = MakeColorAttribute("inColor", wideColor);
+        fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
+        fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
+        this->setVertexAttributes(&fInPosition, 4);
+    }
+
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
         GLSLProcessor() {}
@@ -466,10 +481,10 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             this->setTransformDataHelper(
                     primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
-                    &transformIter);
+                    transformRange);
         }
 
     private:
@@ -488,10 +503,10 @@
 };
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
+GrGeometryProcessor* ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
     bool wideColor = d->fRandom->nextBool();
     const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
-    return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(wideColor, matrix));
+    return ButtCapDashedCircleGeometryProcessor::Make(d->allocator(), wideColor, matrix);
 }
 #endif
 
@@ -507,12 +522,32 @@
 
 class EllipseGeometryProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool stroke, bool wideColor,
+                                     bool useScale, const SkMatrix& localMatrix) {
+        return arena->make<EllipseGeometryProcessor>(stroke, wideColor, useScale, localMatrix);
+    }
+
+    ~EllipseGeometryProcessor() override {}
+
+    const char* name() const override { return "EllipseGeometryProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GLSLProcessor::GenKey(*this, caps, b);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
+        return new GLSLProcessor();
+    }
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
                              const SkMatrix& localMatrix)
-    : INHERITED(kEllipseGeometryProcessor_ClassID)
-    , fLocalMatrix(localMatrix)
-    , fStroke(stroke)
-    , fUseScale(useScale) {
+            : INHERITED(kEllipseGeometryProcessor_ClassID)
+            , fLocalMatrix(localMatrix)
+            , fStroke(stroke)
+            , fUseScale(useScale) {
         fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
         fInColor = MakeColorAttribute("inColor", wideColor);
         if (useScale) {
@@ -524,19 +559,6 @@
         this->setVertexAttributes(&fInPosition, 4);
     }
 
-    ~EllipseGeometryProcessor() override {}
-
-    const char* name() const override { return "EllipseEdge"; }
-
-    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLProcessor::GenKey(*this, caps, b);
-    }
-
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
-        return new GLSLProcessor();
-    }
-
-private:
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
         GLSLProcessor() {}
@@ -650,9 +672,9 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
-            this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
+            this->setTransformDataHelper(egp.fLocalMatrix, pdman, transformRange);
         }
 
     private:
@@ -676,10 +698,10 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
-    return sk_sp<GrGeometryProcessor>(
-            new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
-                                         d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
+GrGeometryProcessor* EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
+    return EllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
+                                          d->fRandom->nextBool(), d->fRandom->nextBool(),
+                                          GrTest::TestMatrix(d->fRandom));
 }
 #endif
 
@@ -698,6 +720,26 @@
 
 class DIEllipseGeometryProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool wideColor, bool useScale,
+                                     const SkMatrix& viewMatrix, DIEllipseStyle style) {
+        return arena->make<DIEllipseGeometryProcessor>(wideColor, useScale, viewMatrix, style);
+    }
+
+    ~DIEllipseGeometryProcessor() override {}
+
+    const char* name() const override { return "DIEllipseGeometryProcessor"; }
+
+    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
+        GLSLProcessor::GenKey(*this, caps, b);
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
+        return new GLSLProcessor();
+    }
+
+private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
                                DIEllipseStyle style)
             : INHERITED(kDIEllipseGeometryProcessor_ClassID)
@@ -717,19 +759,6 @@
         this->setVertexAttributes(&fInPosition, 4);
     }
 
-    ~DIEllipseGeometryProcessor() override {}
-
-    const char* name() const override { return "DIEllipseEdge"; }
-
-    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
-        GLSLProcessor::GenKey(*this, caps, b);
-    }
-
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
-        return new GLSLProcessor();
-    }
-
-private:
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
         GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
@@ -839,7 +868,7 @@
         }
 
         void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
-                     FPCoordTransformIter&& transformIter) override {
+                     const CoordTransformRange& transformRange) override {
             const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
 
             if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
@@ -848,7 +877,7 @@
                 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
                 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
             }
-            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+            this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
         }
 
     private:
@@ -876,10 +905,10 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
 
 #if GR_TEST_UTILS
-sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
-    return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
-            d->fRandom->nextBool(), d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
-            (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
+GrGeometryProcessor* DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
+    return DIEllipseGeometryProcessor::Make(d->allocator(), d->fRandom->nextBool(),
+                                            d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
+                                            (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)));
 }
 #endif
 
@@ -1240,9 +1269,11 @@
         }
 
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
-                !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, fWideColor,
-                localMatrix));
+        GrGeometryProcessor* gp = CircleGeometryProcessor::Make(target->allocator(),
+                                                                !fAllFill, fClipPlane,
+                                                                fClipPlaneIsect, fClipPlaneUnion,
+                                                                fRoundCaps, fWideColor,
+                                                                localMatrix);
 
         sk_sp<const GrBuffer> vertexBuffer;
         int firstVertex;
@@ -1364,7 +1395,7 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangles);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -1572,8 +1603,9 @@
         }
 
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(fWideColor,
-                                                                               localMatrix));
+        GrGeometryProcessor* gp = ButtCapDashedCircleGeometryProcessor::Make(target->allocator(),
+                                                                             fWideColor,
+                                                                             localMatrix);
 
         sk_sp<const GrBuffer> vertexBuffer;
         int firstVertex;
@@ -1654,7 +1686,7 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangles);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -1871,8 +1903,9 @@
         }
 
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
-                                                                   localMatrix));
+        GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(target->allocator(),
+                                                                 fStroked, fWideColor, fUseScale,
+                                                                 localMatrix);
         QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
         GrVertexWriter verts{helper.vertices()};
         if (!verts.fPtr) {
@@ -1908,7 +1941,7 @@
                             GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)),
                             invRadii);
         }
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -2109,9 +2142,9 @@
 private:
     void onPrepareDraws(Target* target) override {
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(
-                new DIEllipseGeometryProcessor(fWideColor, fUseScale, this->viewMatrix(),
-                                               this->style()));
+        GrGeometryProcessor* gp = DIEllipseGeometryProcessor::Make(target->allocator(), fWideColor,
+                                                                   fUseScale, this->viewMatrix(),
+                                                                   this->style());
 
         QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
         GrVertexWriter verts{helper.vertices()};
@@ -2145,7 +2178,7 @@
                             origin_centered_tri_strip(innerRatioX + offsetDx,
                                                       innerRatioY + offsetDy));
         }
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -2485,9 +2518,9 @@
         }
 
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(
-                new CircleGeometryProcessor(!fAllFill, false, false, false, false, fWideColor,
-                                            localMatrix));
+        GrGeometryProcessor* gp = CircleGeometryProcessor::Make(target->allocator(), !fAllFill,
+                                                                false, false, false, false,
+                                                                fWideColor, localMatrix);
 
         sk_sp<const GrBuffer> vertexBuffer;
         int firstVertex;
@@ -2576,7 +2609,7 @@
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangles);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -2777,8 +2810,9 @@
         }
 
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
-                                                                   localMatrix));
+        GrGeometryProcessor* gp = EllipseGeometryProcessor::Make(target->allocator(), fStroked,
+                                                                 fWideColor, fUseScale,
+                                                                 localMatrix);
 
         // drop out the middle quad if we're stroked
         int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
@@ -2791,7 +2825,7 @@
         }
         PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
                              std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
-                             fRRects.count());
+                             fRRects.count(), kNumRRectsInIndexBuffer);
         GrVertexWriter verts{helper.vertices()};
         if (!verts.fPtr) {
             SkDebugf("Could not allocate vertices\n");
@@ -2857,7 +2891,7 @@
                             reciprocalRadii);
             }
         }
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 929824c..da70bf6 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -7,9 +7,9 @@
 
 #include "src/gpu/ops/GrQuadPerEdgeAA.h"
 
-#include "include/private/SkNx.h"
-#include "src/gpu/GrVertexWriter.h"
+#include "include/private/SkVx.h"
 #include "src/gpu/SkGr.h"
+#include "src/gpu/geometry/GrQuadUtils.h"
 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
@@ -17,492 +17,39 @@
 #include "src/gpu/glsl/GrGLSLVarying.h"
 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
 
-#define AI SK_ALWAYS_INLINE
 
 namespace {
 
-// Helper data types since there is a lot of information that needs to be passed around to
-// avoid recalculation in the different procedures for tessellating an AA quad.
-
-using V4f = skvx::Vec<4, float>;
-using M4f = skvx::Vec<4, int32_t>;
-
-struct Vertices {
-    // X, Y, and W coordinates in device space. If not perspective, w should be set to 1.f
-    V4f fX, fY, fW;
-    // U, V, and R coordinates representing local quad. Ignored depending on uvrCount (0, 1, 2).
-    V4f fU, fV, fR;
-    int fUVRCount;
-};
-
-struct QuadMetadata {
-    // Normalized edge vectors of the device space quad, ordered L, B, T, R (i.e. nextCCW(x) - x).
-    V4f fDX, fDY;
-    // 1 / edge length of the device space quad
-    V4f fInvLengths;
-    // Edge mask (set to all 1s if aa flags is kAll), otherwise 1.f if edge was AA, 0.f if non-AA.
-    V4f fMask;
-};
-
-struct Edges {
-    // a * x + b * y + c = 0; positive distance is inside the quad; ordered LBTR.
-    V4f fA, fB, fC;
-    // Whether or not the edge normals had to be flipped to preserve positive distance on the inside
-    bool fFlipped;
-};
-
-static constexpr float kTolerance = 1e-2f;
-// True/false bit masks for initializing an M4f
-static constexpr int32_t kTrue    = ~0;
-static constexpr int32_t kFalse   = 0;
-
-static AI V4f fma(const V4f& f, const V4f& m, const V4f& a) {
-    return mad(f, m, a);
-}
-
-// These rotate the points/edge values either clockwise or counterclockwise assuming tri strip
-// order.
-static AI V4f nextCW(const V4f& v) {
-    return skvx::shuffle<2, 0, 3, 1>(v);
-}
-
-static AI V4f nextCCW(const V4f& v) {
-    return skvx::shuffle<1, 3, 0, 2>(v);
-}
-
-// Replaces zero-length 'bad' edge vectors with the reversed opposite edge vector.
-// e3 may be null if only 2D edges need to be corrected for.
-static AI void correct_bad_edges(const M4f& bad, V4f* e1, V4f* e2, V4f* e3) {
-    if (any(bad)) {
-        // Want opposite edges, L B T R -> R T B L but with flipped sign to preserve winding
-        *e1 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e1), *e1);
-        *e2 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e2), *e2);
-        if (e3) {
-            *e3 = if_then_else(bad, -skvx::shuffle<3, 2, 1, 0>(*e3), *e3);
-        }
-    }
-}
-
-// Replace 'bad' coordinates by rotating CCW to get the next point. c3 may be null for 2D points.
-static AI void correct_bad_coords(const M4f& bad, V4f* c1, V4f* c2, V4f* c3) {
-    if (any(bad)) {
-        *c1 = if_then_else(bad, nextCCW(*c1), *c1);
-        *c2 = if_then_else(bad, nextCCW(*c2), *c2);
-        if (c3) {
-            *c3 = if_then_else(bad, nextCCW(*c3), *c3);
-        }
-    }
-}
-
-static AI QuadMetadata get_metadata(const Vertices& vertices, GrQuadAAFlags aaFlags) {
-    V4f dx = nextCCW(vertices.fX) - vertices.fX;
-    V4f dy = nextCCW(vertices.fY) - vertices.fY;
-    V4f invLengths = rsqrt(fma(dx, dx, dy * dy));
-
-    V4f mask = aaFlags == GrQuadAAFlags::kAll ? V4f(1.f) :
-            V4f{(GrQuadAAFlags::kLeft & aaFlags) ? 1.f : 0.f,
-                 (GrQuadAAFlags::kBottom & aaFlags) ? 1.f : 0.f,
-                 (GrQuadAAFlags::kTop & aaFlags) ? 1.f : 0.f,
-                 (GrQuadAAFlags::kRight & aaFlags) ? 1.f : 0.f};
-    return { dx * invLengths, dy * invLengths, invLengths, mask };
-}
-
-static AI Edges get_edge_equations(const QuadMetadata& metadata, const Vertices& vertices) {
-    V4f dx = metadata.fDX;
-    V4f dy = metadata.fDY;
-    // Correct for bad edges by copying adjacent edge information into the bad component
-    correct_bad_edges(metadata.fInvLengths >= 1.f / kTolerance, &dx, &dy, nullptr);
-
-    V4f c = fma(dx, vertices.fY, -dy * vertices.fX);
-    // Make sure normals point into the shape
-    V4f test = fma(dy, nextCW(vertices.fX), fma(-dx, nextCW(vertices.fY), c));
-    if (any(test < -kTolerance)) {
-        return {-dy, dx, -c, true};
-    } else {
-        return {dy, -dx, c, false};
-    }
-}
-
-// Sets 'outset' to the magnitude of outset/inset to adjust each corner of a quad given the
-// edge angles and lengths. If the quad is too small, has empty edges, or too sharp of angles,
-// false is returned and the degenerate slow-path should be used.
-static bool get_optimized_outset(const QuadMetadata& metadata, bool rectilinear, V4f* outset) {
-    if (rectilinear) {
-        *outset = 0.5f;
-        // Stay in the fast path as long as all edges are at least a pixel long (so 1/len <= 1)
-        return all(metadata.fInvLengths <= 1.f);
-    }
-
-    if (any(metadata.fInvLengths >= 1.f / kTolerance)) {
-        // Have an empty edge from a degenerate quad, so there's no hope
-        return false;
-    }
-
-    // The distance the point needs to move is 1/2sin(theta), where theta is the angle between the
-    // two edges at that point. cos(theta) is equal to dot(dxy, nextCW(dxy))
-    V4f cosTheta = fma(metadata.fDX, nextCW(metadata.fDX), metadata.fDY * nextCW(metadata.fDY));
-    // If the angle is too shallow between edges, go through the degenerate path, otherwise adding
-    // and subtracting very large vectors in almost opposite directions leads to float errors
-    if (any(abs(cosTheta) >= 0.9f)) {
-        return false;
-    }
-    *outset = 0.5f * rsqrt(1.f - cosTheta * cosTheta); // 1/2sin(theta)
-
-    // When outsetting or insetting, the current edge's AA adds to the length:
-    //   cos(pi - theta)/2sin(theta) + cos(pi-ccw(theta))/2sin(ccw(theta))
-    // Moving an adjacent edge updates the length by 1/2sin(theta|ccw(theta))
-    V4f halfTanTheta = -cosTheta * (*outset); // cos(pi - theta) = -cos(theta)
-    V4f edgeAdjust = metadata.fMask * (halfTanTheta + nextCCW(halfTanTheta)) +
-                      nextCCW(metadata.fMask) * nextCCW(*outset) +
-                      nextCW(metadata.fMask) * (*outset);
-    // If either outsetting (plus edgeAdjust) or insetting (minus edgeAdjust) make edgeLen negative
-    // then use the slow path
-    V4f threshold = 0.1f - (1.f / metadata.fInvLengths);
-    return all(edgeAdjust > threshold) && all(edgeAdjust < -threshold);
-}
-
-// Ignores the quad's fW, use outset_projected_vertices if it's known to need 3D.
-static AI void outset_vertices(const V4f& outset, const QuadMetadata& metadata, Vertices* quad) {
-    // The mask is rotated compared to the outsets and edge vectors, since if the edge is "on"
-    // both its points need to be moved along their other edge vectors.
-    auto maskedOutset = -outset * nextCW(metadata.fMask);
-    auto maskedOutsetCW = outset * metadata.fMask;
-    // x = x + outset * mask * nextCW(xdiff) - outset * nextCW(mask) * xdiff
-    quad->fX += fma(maskedOutsetCW, nextCW(metadata.fDX), maskedOutset * metadata.fDX);
-    quad->fY += fma(maskedOutsetCW, nextCW(metadata.fDY), maskedOutset * metadata.fDY);
-    if (quad->fUVRCount > 0) {
-        // We want to extend the texture coords by the same proportion as the positions.
-        maskedOutset *= metadata.fInvLengths;
-        maskedOutsetCW *= nextCW(metadata.fInvLengths);
-        V4f du = nextCCW(quad->fU) - quad->fU;
-        V4f dv = nextCCW(quad->fV) - quad->fV;
-        quad->fU += fma(maskedOutsetCW, nextCW(du), maskedOutset * du);
-        quad->fV += fma(maskedOutsetCW, nextCW(dv), maskedOutset * dv);
-        if (quad->fUVRCount == 3) {
-            V4f dr = nextCCW(quad->fR) - quad->fR;
-            quad->fR += fma(maskedOutsetCW, nextCW(dr), maskedOutset * dr);
-        }
-    }
-}
-
-// Updates (x,y,w) to be at (x2d,y2d) once projected. Updates (u,v,r) to match if provided.
-// Gracefully handles 2D content if *w holds all 1s.
-static void outset_projected_vertices(const V4f& x2d, const V4f& y2d,
-                                      GrQuadAAFlags aaFlags, Vertices* quad) {
-    // Left to right, in device space, for each point
-    V4f e1x = skvx::shuffle<2, 3, 2, 3>(quad->fX) - skvx::shuffle<0, 1, 0, 1>(quad->fX);
-    V4f e1y = skvx::shuffle<2, 3, 2, 3>(quad->fY) - skvx::shuffle<0, 1, 0, 1>(quad->fY);
-    V4f e1w = skvx::shuffle<2, 3, 2, 3>(quad->fW) - skvx::shuffle<0, 1, 0, 1>(quad->fW);
-    correct_bad_edges(fma(e1x, e1x, e1y * e1y) < kTolerance * kTolerance, &e1x, &e1y, &e1w);
-
-    // // Top to bottom, in device space, for each point
-    V4f e2x = skvx::shuffle<1, 1, 3, 3>(quad->fX) - skvx::shuffle<0, 0, 2, 2>(quad->fX);
-    V4f e2y = skvx::shuffle<1, 1, 3, 3>(quad->fY) - skvx::shuffle<0, 0, 2, 2>(quad->fY);
-    V4f e2w = skvx::shuffle<1, 1, 3, 3>(quad->fW) - skvx::shuffle<0, 0, 2, 2>(quad->fW);
-    correct_bad_edges(fma(e2x, e2x, e2y * e2y) < kTolerance * kTolerance, &e2x, &e2y, &e2w);
-
-    // Can only move along e1 and e2 to reach the new 2D point, so we have
-    // x2d = (x + a*e1x + b*e2x) / (w + a*e1w + b*e2w) and
-    // y2d = (y + a*e1y + b*e2y) / (w + a*e1w + b*e2w) for some a, b
-    // This can be rewritten to a*c1x + b*c2x + c3x = 0; a * c1y + b*c2y + c3y = 0, where
-    // the cNx and cNy coefficients are:
-    V4f c1x = e1w * x2d - e1x;
-    V4f c1y = e1w * y2d - e1y;
-    V4f c2x = e2w * x2d - e2x;
-    V4f c2y = e2w * y2d - e2y;
-    V4f c3x = quad->fW * x2d - quad->fX;
-    V4f c3y = quad->fW * y2d - quad->fY;
-
-    // Solve for a and b
-    V4f a, b, denom;
-    if (aaFlags == GrQuadAAFlags::kAll) {
-        // When every edge is outset/inset, each corner can use both edge vectors
-        denom = c1x * c2y - c2x * c1y;
-        a = (c2x * c3y - c3x * c2y) / denom;
-        b = (c3x * c1y - c1x * c3y) / denom;
-    } else {
-        // Force a or b to be 0 if that edge cannot be used due to non-AA
-        M4f aMask = M4f{(aaFlags & GrQuadAAFlags::kLeft)   ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kLeft)   ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kRight)  ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kRight)  ? kTrue : kFalse};
-        M4f bMask = M4f{(aaFlags & GrQuadAAFlags::kTop)    ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kTop)    ? kTrue : kFalse,
-                        (aaFlags & GrQuadAAFlags::kBottom) ? kTrue : kFalse};
-
-        // When aMask[i]&bMask[i], then a[i], b[i], denom[i] match the kAll case.
-        // When aMask[i]&!bMask[i], then b[i] = 0, a[i] = -c3x/c1x or -c3y/c1y, using better denom
-        // When !aMask[i]&bMask[i], then a[i] = 0, b[i] = -c3x/c2x or -c3y/c2y, ""
-        // When !aMask[i]&!bMask[i], then both a[i] = 0 and b[i] = 0
-        M4f useC1x = abs(c1x) > abs(c1y);
-        M4f useC2x = abs(c2x) > abs(c2y);
-
-        denom = if_then_else(aMask,
-                        if_then_else(bMask,
-                                c1x * c2y - c2x * c1y,            /* A & B   */
-                                if_then_else(useC1x, c1x, c1y)),  /* A & !B  */
-                        if_then_else(bMask,
-                                if_then_else(useC2x, c2x, c2y),   /* !A & B  */
-                                V4f(1.f)));                       /* !A & !B */
-
-        a = if_then_else(aMask,
-                    if_then_else(bMask,
-                            c2x * c3y - c3x * c2y,                /* A & B   */
-                            if_then_else(useC1x, -c3x, -c3y)),    /* A & !B  */
-                    V4f(0.f)) / denom;                            /* !A      */
-        b = if_then_else(bMask,
-                    if_then_else(aMask,
-                            c3x * c1y - c1x * c3y,                /* A & B   */
-                            if_then_else(useC2x, -c3x, -c3y)),    /* !A & B  */
-                    V4f(0.f)) / denom;                            /* !B      */
-    }
-
-    V4f newW = quad->fW + a * e1w + b * e2w;
-    // If newW < 0, scale a and b such that the point reaches the infinity plane instead of crossing
-    // This breaks orthogonality of inset/outsets, but GPUs don't handle negative Ws well so this
-    // is far less visually disturbing (likely not noticeable since it's at extreme perspective).
-    // The alternative correction (multiply xyw by -1) has the disadvantage of changing how local
-    // coordinates would be interpolated.
-    static const float kMinW = 1e-6f;
-    if (any(newW < 0.f)) {
-        V4f scale = if_then_else(newW < kMinW, (kMinW - quad->fW) / (newW - quad->fW), V4f(1.f));
-        a *= scale;
-        b *= scale;
-    }
-
-    quad->fX += a * e1x + b * e2x;
-    quad->fY += a * e1y + b * e2y;
-    quad->fW += a * e1w + b * e2w;
-    correct_bad_coords(abs(denom) < kTolerance, &quad->fX, &quad->fY, &quad->fW);
-
-    if (quad->fUVRCount > 0) {
-        // Calculate R here so it can be corrected with U and V in case it's needed later
-        V4f e1u = skvx::shuffle<2, 3, 2, 3>(quad->fU) - skvx::shuffle<0, 1, 0, 1>(quad->fU);
-        V4f e1v = skvx::shuffle<2, 3, 2, 3>(quad->fV) - skvx::shuffle<0, 1, 0, 1>(quad->fV);
-        V4f e1r = skvx::shuffle<2, 3, 2, 3>(quad->fR) - skvx::shuffle<0, 1, 0, 1>(quad->fR);
-        correct_bad_edges(fma(e1u, e1u, e1v * e1v) < kTolerance * kTolerance, &e1u, &e1v, &e1r);
-
-        V4f e2u = skvx::shuffle<1, 1, 3, 3>(quad->fU) - skvx::shuffle<0, 0, 2, 2>(quad->fU);
-        V4f e2v = skvx::shuffle<1, 1, 3, 3>(quad->fV) - skvx::shuffle<0, 0, 2, 2>(quad->fV);
-        V4f e2r = skvx::shuffle<1, 1, 3, 3>(quad->fR) - skvx::shuffle<0, 0, 2, 2>(quad->fR);
-        correct_bad_edges(fma(e2u, e2u, e2v * e2v) < kTolerance * kTolerance, &e2u, &e2v, &e2r);
-
-        quad->fU += a * e1u + b * e2u;
-        quad->fV += a * e1v + b * e2v;
-        if (quad->fUVRCount == 3) {
-            quad->fR += a * e1r + b * e2r;
-            correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, &quad->fR);
-        } else {
-            correct_bad_coords(abs(denom) < kTolerance, &quad->fU, &quad->fV, nullptr);
-        }
-    }
-}
-
-static V4f degenerate_coverage(const V4f& px, const V4f& py, const Edges& edges) {
-    // Calculate distance of the 4 inset points (px, py) to the 4 edges
-    V4f d0 = fma(edges.fA[0], px, fma(edges.fB[0], py, edges.fC[0]));
-    V4f d1 = fma(edges.fA[1], px, fma(edges.fB[1], py, edges.fC[1]));
-    V4f d2 = fma(edges.fA[2], px, fma(edges.fB[2], py, edges.fC[2]));
-    V4f d3 = fma(edges.fA[3], px, fma(edges.fB[3], py, edges.fC[3]));
-
-    // For each point, pretend that there's a rectangle that touches e0 and e3 on the horizontal
-    // axis, so its width is "approximately" d0 + d3, and it touches e1 and e2 on the vertical axis
-    // so its height is d1 + d2. Pin each of these dimensions to [0, 1] and approximate the coverage
-    // at each point as clamp(d0+d3, 0, 1) x clamp(d1+d2, 0, 1). For rectilinear quads this is an
-    // accurate calculation of its area clipped to an aligned pixel. For arbitrary quads it is not
-    // mathematically accurate but qualitatively provides a stable value proportional to the size of
-    // the shape.
-    V4f w = max(0.f, min(1.f, d0 + d3));
-    V4f h = max(0.f, min(1.f, d1 + d2));
-    return w * h;
-}
-
-// Outsets or insets xs/ys in place. To be used when the interior is very small, edges are near
-// parallel, or edges are very short/zero-length. Returns coverage for each vertex.
-// Requires (dx, dy) to already be fixed for empty edges.
-static V4f compute_degenerate_quad(GrQuadAAFlags aaFlags, const V4f& mask, const Edges& edges,
-                                   bool outset, Vertices* quad) {
-    // Move the edge 1/2 pixel in or out depending on 'outset'.
-    V4f oc = edges.fC + mask * (outset ? 0.5f : -0.5f);
-
-    // There are 6 points that we care about to determine the final shape of the polygon, which
-    // are the intersections between (e0,e2), (e1,e0), (e2,e3), (e3,e1) (corresponding to the
-    // 4 corners), and (e1, e2), (e0, e3) (representing the intersections of opposite edges).
-    V4f denom = edges.fA * nextCW(edges.fB) - edges.fB * nextCW(edges.fA);
-    V4f px = (edges.fB * nextCW(oc) - oc * nextCW(edges.fB)) / denom;
-    V4f py = (oc * nextCW(edges.fA) - edges.fA * nextCW(oc)) / denom;
-    correct_bad_coords(abs(denom) < kTolerance, &px, &py, nullptr);
-
-    // Calculate the signed distances from these 4 corners to the other two edges that did not
-    // define the intersection. So p(0) is compared to e3,e1, p(1) to e3,e2 , p(2) to e0,e1, and
-    // p(3) to e0,e2
-    V4f dists1 = px * skvx::shuffle<3, 3, 0, 0>(edges.fA) +
-                 py * skvx::shuffle<3, 3, 0, 0>(edges.fB) +
-                 skvx::shuffle<3, 3, 0, 0>(oc);
-    V4f dists2 = px * skvx::shuffle<1, 2, 1, 2>(edges.fA) +
-                 py * skvx::shuffle<1, 2, 1, 2>(edges.fB) +
-                 skvx::shuffle<1, 2, 1, 2>(oc);
-
-    // If all the distances are >= 0, the 4 corners form a valid quadrilateral, so use them as
-    // the 4 points. If any point is on the wrong side of both edges, the interior has collapsed
-    // and we need to use a central point to represent it. If all four points are only on the
-    // wrong side of 1 edge, one edge has crossed over another and we use a line to represent it.
-    // Otherwise, use a triangle that replaces the bad points with the intersections of
-    // (e1, e2) or (e0, e3) as needed.
-    M4f d1v0 = dists1 < kTolerance;
-    M4f d2v0 = dists2 < kTolerance;
-    M4f d1And2 = d1v0 & d2v0;
-    M4f d1Or2 = d1v0 | d2v0;
-
-    V4f coverage;
-    if (!any(d1Or2)) {
-        // Every dists1 and dists2 >= kTolerance so it's not degenerate, use all 4 corners as-is
-        // and use full coverage
-        coverage = 1.f;
-    } else if (any(d1And2)) {
-        // A point failed against two edges, so reduce the shape to a single point, which we take as
-        // the center of the original quad to ensure it is contained in the intended geometry. Since
-        // it has collapsed, we know the shape cannot cover a pixel so update the coverage.
-        SkPoint center = {0.25f * (quad->fX[0] + quad->fX[1] + quad->fX[2] + quad->fX[3]),
-                          0.25f * (quad->fY[0] + quad->fY[1] + quad->fY[2] + quad->fY[3])};
-        px = center.fX;
-        py = center.fY;
-        coverage = degenerate_coverage(px, py, edges);
-    } else if (all(d1Or2)) {
-        // Degenerates to a line. Compare p[2] and p[3] to edge 0. If they are on the wrong side,
-        // that means edge 0 and 3 crossed, and otherwise edge 1 and 2 crossed.
-        if (dists1[2] < kTolerance && dists1[3] < kTolerance) {
-            // Edges 0 and 3 have crossed over, so make the line from average of (p0,p2) and (p1,p3)
-            px = 0.5f * (skvx::shuffle<0, 1, 0, 1>(px) + skvx::shuffle<2, 3, 2, 3>(px));
-            py = 0.5f * (skvx::shuffle<0, 1, 0, 1>(py) + skvx::shuffle<2, 3, 2, 3>(py));
-        } else {
-            // Edges 1 and 2 have crossed over, so make the line from average of (p0,p1) and (p2,p3)
-            px = 0.5f * (skvx::shuffle<0, 0, 2, 2>(px) + skvx::shuffle<1, 1, 3, 3>(px));
-            py = 0.5f * (skvx::shuffle<0, 0, 2, 2>(py) + skvx::shuffle<1, 1, 3, 3>(py));
-        }
-        coverage = degenerate_coverage(px, py, edges);
-    } else {
-        // This turns into a triangle. Replace corners as needed with the intersections between
-        // (e0,e3) and (e1,e2), which must now be calculated
-        using V2f = skvx::Vec<2, float>;
-        V2f eDenom = skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(edges.fB) -
-                      skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(edges.fA);
-        V2f ex = (skvx::shuffle<0, 1>(edges.fB) * skvx::shuffle<3, 2>(oc) -
-                   skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fB)) / eDenom;
-        V2f ey = (skvx::shuffle<0, 1>(oc) * skvx::shuffle<3, 2>(edges.fA) -
-                   skvx::shuffle<0, 1>(edges.fA) * skvx::shuffle<3, 2>(oc)) / eDenom;
-
-        if (SkScalarAbs(eDenom[0]) > kTolerance) {
-            px = if_then_else(d1v0, V4f(ex[0]), px);
-            py = if_then_else(d1v0, V4f(ey[0]), py);
-        }
-        if (SkScalarAbs(eDenom[1]) > kTolerance) {
-            px = if_then_else(d2v0, V4f(ex[1]), px);
-            py = if_then_else(d2v0, V4f(ey[1]), py);
-        }
-
-        coverage = 1.f;
-    }
-
-    outset_projected_vertices(px, py, aaFlags, quad);
-    return coverage;
-}
-
-// Computes the vertices for the two nested quads used to create AA edges. The original single quad
-// should be duplicated as input in 'inner' and 'outer', and the resulting quad frame will be
-// stored in-place on return. Returns per-vertex coverage for the inner vertices.
-static V4f compute_nested_quad_vertices(GrQuadAAFlags aaFlags, bool rectilinear,
-                                         Vertices* inner, Vertices* outer, SkRect* domain) {
-    SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
-    SkASSERT(outer->fUVRCount == inner->fUVRCount);
-
-    QuadMetadata metadata = get_metadata(*inner, aaFlags);
-
-    // Calculate domain first before updating vertices. It's only used when not rectilinear.
-    if (!rectilinear) {
-        SkASSERT(domain);
-        // The domain is the bounding box of the quad, outset by 0.5. Don't worry about edge masks
-        // since the FP only applies the domain on the exterior triangles, which are degenerate for
-        // non-AA edges.
-        domain->fLeft = min(outer->fX) - 0.5f;
-        domain->fRight = max(outer->fX) + 0.5f;
-        domain->fTop = min(outer->fY) - 0.5f;
-        domain->fBottom = max(outer->fY) + 0.5f;
-    }
-
-    // When outsetting, we want the new edge to be .5px away from the old line, which means the
-    // corners may need to be adjusted by more than .5px if the matrix had sheer. This adjustment
-    // is only computed if there are no empty edges, and it may signal going through the slow path.
-    V4f outset = 0.5f;
-    if (get_optimized_outset(metadata, rectilinear, &outset)) {
-       // Since it's not subpixel, outsetting and insetting are trivial vector additions.
-        outset_vertices(outset, metadata, outer);
-        outset_vertices(-outset, metadata, inner);
-        return 1.f;
-    }
-
-    // Only compute edge equations once since they are the same for inner and outer quads
-    Edges edges = get_edge_equations(metadata, *inner);
-
-    // Calculate both outset and inset, returning the coverage reported for the inset, since the
-    // outset will always have 0.0f.
-    compute_degenerate_quad(aaFlags, metadata.fMask, edges, true, outer);
-    return compute_degenerate_quad(aaFlags, metadata.fMask, edges, false, inner);
-}
-
-// Generalizes compute_nested_quad_vertices to extrapolate local coords such that after perspective
-// division of the device coordinates, the original local coordinate value is at the original
-// un-outset device position.
-static V4f compute_nested_persp_quad_vertices(const GrQuadAAFlags aaFlags, Vertices* inner,
-                                               Vertices* outer, SkRect* domain) {
-    SkASSERT(inner->fUVRCount == 0 || inner->fUVRCount == 2 || inner->fUVRCount == 3);
-    SkASSERT(outer->fUVRCount == inner->fUVRCount);
-
-    // Calculate the projected 2D quad and use it to form projeccted inner/outer quads
-    V4f iw = 1.0f / inner->fW;
-    V4f x2d = inner->fX * iw;
-    V4f y2d = inner->fY * iw;
-
-    Vertices inner2D = { x2d, y2d, /*w*/ 1.f, 0.f, 0.f, 0.f, 0 }; // No uvr outsetting in 2D
-    Vertices outer2D = inner2D;
-
-    V4f coverage = compute_nested_quad_vertices(
-            aaFlags, /* rect */ false, &inner2D, &outer2D, domain);
-
-    // Now map from the 2D inset/outset back to 3D and update the local coordinates as well
-    outset_projected_vertices(inner2D.fX, inner2D.fY, aaFlags, inner);
-    outset_projected_vertices(outer2D.fX, outer2D.fY, aaFlags, outer);
-
-    return coverage;
-}
-
-// Writes four vertices in triangle strip order, including the additional data for local
-// coordinates, geometry + texture domains, color, and coverage as needed to satisfy the vertex spec
-static void write_quad(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
-                       GrQuadPerEdgeAA::CoverageMode mode, const V4f& coverage, SkPMColor4f color4f,
-                       const SkRect& geomDomain, const SkRect& texDomain, const Vertices& quad) {
+// Generic WriteQuadProc that can handle any VertexSpec. It writes the 4 vertices in triangle strip
+// order, although the data per-vertex is dependent on the VertexSpec.
+static void write_quad_generic(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                               const GrQuad* deviceQuad, const GrQuad* localQuad,
+                               const float coverage[4], const SkPMColor4f& color,
+                               const SkRect& geomDomain, const SkRect& texDomain) {
     static constexpr auto If = GrVertexWriter::If<float>;
 
+    SkASSERT(!spec.hasLocalCoords() || localQuad);
+
+    GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
     for (int i = 0; i < 4; ++i) {
         // save position, this is a float2 or float3 or float4 depending on the combination of
         // perspective and coverage mode.
-        vb->write(quad.fX[i], quad.fY[i],
-                  If(spec.deviceQuadType() == GrQuad::Type::kPerspective, quad.fW[i]),
+        vb->write(deviceQuad->x(i), deviceQuad->y(i),
+                  If(spec.deviceQuadType() == GrQuad::Type::kPerspective, deviceQuad->w(i)),
                   If(mode == GrQuadPerEdgeAA::CoverageMode::kWithPosition, coverage[i]));
 
         // save color
         if (spec.hasVertexColors()) {
             bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
             vb->write(GrVertexColor(
-                color4f * (mode == GrQuadPerEdgeAA::CoverageMode::kWithColor ? coverage[i] : 1.f),
+                color * (mode == GrQuadPerEdgeAA::CoverageMode::kWithColor ? coverage[i] : 1.f),
                 wide));
         }
 
         // save local position
         if (spec.hasLocalCoords()) {
-            vb->write(quad.fU[i], quad.fV[i],
-                      If(spec.localQuadType() == GrQuad::Type::kPerspective, quad.fR[i]));
+            vb->write(localQuad->x(i), localQuad->y(i),
+                      If(spec.localQuadType() == GrQuad::Type::kPerspective, localQuad->w(i)));
         }
 
         // save the geometry domain
@@ -517,34 +64,190 @@
     }
 }
 
-GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
+// Specialized WriteQuadProcs for particular VertexSpecs that show up frequently (determined
+// experimentally through recorded GMs, SKPs, and SVGs, as well as SkiaRenderer's usage patterns):
 
-static const int kVertsPerAAFillRect = 8;
-static const int kIndicesPerAAFillRect = 30;
+// 2D (XY), no explicit coverage, vertex color, no locals, no geometry domain, no texture domain
+// This represents simple, solid color or shader, non-AA (or AA with cov. as alpha) rects.
+static void write_2d_color(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                           const GrQuad* deviceQuad, const GrQuad* localQuad,
+                           const float coverage[4], const SkPMColor4f& color,
+                           const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(!spec.hasLocalCoords());
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
+             spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
+    SkASSERT(spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(!spec.hasDomain());
+    // We don't assert that localQuad == nullptr, since it is possible for GrFillRectOp to
+    // accumulate local coords conservatively (paint not trivial), and then after analysis realize
+    // the processors don't need local coordinates.
 
-static sk_sp<const GrGpuBuffer> get_index_buffer(GrResourceProvider* resourceProvider) {
-    GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
+    bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
+    for (int i = 0; i < 4; ++i) {
+        // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
+        SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
+                 coverage[i] == 1.f);
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide));
+    }
+}
 
-    // clang-format off
-    static const uint16_t gFillAARectIdx[] = {
-        0, 1, 2, 1, 3, 2,
-        0, 4, 1, 4, 5, 1,
-        0, 6, 4, 0, 2, 6,
-        2, 3, 6, 3, 7, 6,
-        1, 5, 3, 3, 5, 7,
-    };
-    // clang-format on
+// 2D (XY), no explicit coverage, UV locals, no color, no geometry domain, no texture domain
+// This represents opaque, non AA, textured rects
+static void write_2d_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                        const GrQuad* deviceQuad, const GrQuad* localQuad,
+                        const float coverage[4], const SkPMColor4f& color,
+                        const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
+    SkASSERT(!spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(!spec.hasDomain());
+    SkASSERT(localQuad);
 
-    GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
-    return resourceProvider->findOrCreatePatternedIndexBuffer(
-            gFillAARectIdx, kIndicesPerAAFillRect, GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer,
-            kVertsPerAAFillRect, gAAFillRectIndexBufferKey);
+    for (int i = 0; i < 4; ++i) {
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i));
+    }
+}
+
+// 2D (XY), no explicit coverage, UV locals, vertex color, no geometry or texture domains
+// This represents transparent, non AA (or AA with cov. as alpha), textured rects
+static void write_2d_color_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                              const GrQuad* deviceQuad, const GrQuad* localQuad,
+                              const float coverage[4], const SkPMColor4f& color,
+                              const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
+             spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
+    SkASSERT(spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(!spec.hasDomain());
+    SkASSERT(localQuad);
+
+    bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
+    for (int i = 0; i < 4; ++i) {
+        // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
+        SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
+                 coverage[i] == 1.f);
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
+                  localQuad->x(i), localQuad->y(i));
+    }
+}
+
+// 2D (XY), explicit coverage, UV locals, no color, no geometry domain, no texture domain
+// This represents opaque, AA, textured rects
+static void write_2d_cov_uv(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                            const GrQuad* deviceQuad, const GrQuad* localQuad,
+                            const float coverage[4], const SkPMColor4f& color,
+                            const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
+    SkASSERT(!spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(!spec.hasDomain());
+    SkASSERT(localQuad);
+
+    for (int i = 0; i < 4; ++i) {
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
+                  localQuad->x(i), localQuad->y(i));
+    }
+}
+
+// NOTE: The three _strict specializations below match the non-strict uv functions above, except
+// that they also write the UV domain. These are included to benefit SkiaRenderer, which must make
+// use of both fast and strict constrained domains. When testing _strict was not that common across
+// GMS, SKPs, and SVGs but we have little visibility into actual SkiaRenderer statistics. If
+// SkiaRenderer can avoid domains more, these 3 functions should probably be removed for simplicity.
+
+// 2D (XY), no explicit coverage, UV locals, no color, tex domain but no geometry domain
+// This represents opaque, non AA, textured rects with strict uv sampling
+static void write_2d_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                               const GrQuad* deviceQuad, const GrQuad* localQuad,
+                               const float coverage[4], const SkPMColor4f& color,
+                               const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone);
+    SkASSERT(!spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(spec.hasDomain());
+    SkASSERT(localQuad);
+
+    for (int i = 0; i < 4; ++i) {
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), localQuad->x(i), localQuad->y(i), texDomain);
+    }
+}
+
+// 2D (XY), no explicit coverage, UV locals, vertex color, tex domain but no geometry domain
+// This represents transparent, non AA (or AA with cov. as alpha), textured rects with strict sample
+static void write_2d_color_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                                     const GrQuad* deviceQuad, const GrQuad* localQuad,
+                                     const float coverage[4], const SkPMColor4f& color,
+                                     const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kNone ||
+             spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor);
+    SkASSERT(spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(spec.hasDomain());
+    SkASSERT(localQuad);
+
+    bool wide = spec.colorType() == GrQuadPerEdgeAA::ColorType::kHalf;
+    for (int i = 0; i < 4; ++i) {
+        // If this is not coverage-with-alpha, make sure coverage == 1 so it doesn't do anything
+        SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithColor ||
+                 coverage[i] == 1.f);
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), GrVertexColor(color * coverage[i], wide),
+                  localQuad->x(i), localQuad->y(i), texDomain);
+    }
+}
+
+// 2D (XY), explicit coverage, UV locals, no color, tex domain but no geometry domain
+// This represents opaque, AA, textured rects with strict uv sampling
+static void write_2d_cov_uv_strict(GrVertexWriter* vb, const GrQuadPerEdgeAA::VertexSpec& spec,
+                                   const GrQuad* deviceQuad, const GrQuad* localQuad,
+                                   const float coverage[4], const SkPMColor4f& color,
+                                   const SkRect& geomDomain, const SkRect& texDomain) {
+    // Assert assumptions about VertexSpec
+    SkASSERT(spec.deviceQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective);
+    SkASSERT(spec.coverageMode() == GrQuadPerEdgeAA::CoverageMode::kWithPosition);
+    SkASSERT(!spec.hasVertexColors());
+    SkASSERT(!spec.requiresGeometryDomain());
+    SkASSERT(spec.hasDomain());
+    SkASSERT(localQuad);
+
+    for (int i = 0; i < 4; ++i) {
+        vb->write(deviceQuad->x(i), deviceQuad->y(i), coverage[i],
+                  localQuad->x(i), localQuad->y(i), texDomain);
+    }
 }
 
 } // anonymous namespace
 
 namespace GrQuadPerEdgeAA {
 
+IndexBufferOption CalcIndexBufferOption(GrAAType aa, int numQuads) {
+    if (aa == GrAAType::kCoverage) {
+        return IndexBufferOption::kPictureFramed;
+    } else if (numQuads > 1) {
+        return IndexBufferOption::kIndexedRects;
+    } else {
+        return IndexBufferOption::kTriStrips;
+    }
+}
+
 // This is a more elaborate version of SkPMColor4fNeedsWideColor that allows "no color" for white
 ColorType MinColorType(SkPMColor4f color, GrClampType clampType, const GrCaps& caps) {
     if (color == SK_PMColor4fWHITE) {
@@ -555,103 +258,199 @@
     }
 }
 
-////////////////// Tessellate Implementation
+////////////////// Tessellator Implementation
 
-void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQuad,
-                 const SkPMColor4f& color4f, const GrQuad& localQuad, const SkRect& domain,
-                 GrQuadAAFlags aaFlags) {
-    SkASSERT(deviceQuad.quadType() <= spec.deviceQuadType());
-    SkASSERT(!spec.hasLocalCoords() || localQuad.quadType() <= spec.localQuadType());
-
-    GrQuadPerEdgeAA::CoverageMode mode = spec.coverageMode();
-
-    // Load position data into V4fs (always x, y, and load w to avoid branching down the road)
-    Vertices outer;
-    outer.fX = deviceQuad.x4f();
-    outer.fY = deviceQuad.y4f();
-    outer.fW = deviceQuad.w4f(); // Guaranteed to be 1f if it's not perspective
-
-    // Load local position data into V4fs (either none, just u,v or all three)
-    outer.fUVRCount = spec.localDimensionality();
-    if (spec.hasLocalCoords()) {
-        outer.fU = localQuad.x4f();
-        outer.fV = localQuad.y4f();
-        outer.fR = localQuad.w4f(); // Will be ignored if the local quad type isn't perspective
-    }
-
-    GrVertexWriter vb{vertices};
-    if (spec.usesCoverageAA()) {
-        SkASSERT(mode == CoverageMode::kWithPosition || mode == CoverageMode::kWithColor);
-        // Must calculate two new quads, an outset and inset by .5 in projected device space, so
-        // duplicate the original quad for the inner space
-        Vertices inner = outer;
-
-        SkRect geomDomain;
-        V4f maxCoverage = 1.f;
-        if (spec.deviceQuadType() == GrQuad::Type::kPerspective) {
-            // For perspective, send quads with all edges non-AA through the tessellation to ensure
-            // their corners are processed the same as adjacent quads. This approach relies on
-            // solving edge equations to reconstruct corners, which can create seams if an inner
-            // fully non-AA quad is not similarly processed.
-            maxCoverage = compute_nested_persp_quad_vertices(aaFlags, &inner, &outer, &geomDomain);
-        } else if (aaFlags != GrQuadAAFlags::kNone) {
-            // In 2D, the simpler corner math does not cause issues with seaming against non-AA
-            // inner quads.
-            maxCoverage = compute_nested_quad_vertices(
-                    aaFlags, spec.deviceQuadType() <= GrQuad::Type::kRectilinear, &inner, &outer,
-                    &geomDomain);
-        } else if (spec.requiresGeometryDomain()) {
-            // The quad itself wouldn't need a geometric domain, but the batch does, so set the
-            // domain to the bounds of the X/Y coords. Since it's non-AA, this won't actually be
-            // evaluated by the shader, but make sure not to upload uninitialized data.
-            geomDomain.fLeft = min(outer.fX);
-            geomDomain.fRight = max(outer.fX);
-            geomDomain.fTop = min(outer.fY);
-            geomDomain.fBottom = max(outer.fY);
+Tessellator::WriteQuadProc Tessellator::GetWriteQuadProc(const VertexSpec& spec) {
+    // All specialized writing functions requires 2D geometry and no geometry domain. This is not
+    // the same as just checking device type vs. kRectilinear since non-AA general 2D quads do not
+    // require a geometry domain and could then go through a fast path.
+    if (spec.deviceQuadType() != GrQuad::Type::kPerspective && !spec.requiresGeometryDomain()) {
+        CoverageMode mode = spec.coverageMode();
+        if (spec.hasVertexColors()) {
+            if (mode != CoverageMode::kWithPosition) {
+                // Vertex colors, but no explicit coverage
+                if (!spec.hasLocalCoords()) {
+                    // Non-UV with vertex colors (possibly with coverage folded into alpha)
+                    return write_2d_color;
+                } else if (spec.localQuadType() != GrQuad::Type::kPerspective) {
+                    // UV locals with vertex colors (possibly with coverage-as-alpha)
+                    return spec.hasDomain() ? write_2d_color_uv_strict : write_2d_color_uv;
+                }
+            }
+            // Else fall through; this is a spec that requires vertex colors and explicit coverage,
+            // which means it's anti-aliased and the FPs don't support coverage as alpha, or
+            // it uses 3D local coordinates.
+        } else if (spec.hasLocalCoords() && spec.localQuadType() != GrQuad::Type::kPerspective) {
+            if (mode == CoverageMode::kWithPosition) {
+                // UV locals with explicit coverage
+                return spec.hasDomain() ? write_2d_cov_uv_strict : write_2d_cov_uv;
+            } else {
+                SkASSERT(mode == CoverageMode::kNone);
+                return spec.hasDomain() ? write_2d_uv_strict : write_2d_uv;
+            }
         }
-
-        // Write two quads for inner and outer, inner will use the
-        write_quad(&vb, spec, mode, maxCoverage, color4f, geomDomain, domain, inner);
-        write_quad(&vb, spec, mode, 0.f, color4f, geomDomain, domain, outer);
-    } else {
-        // No outsetting needed, just write a single quad with full coverage
-        SkASSERT(mode == CoverageMode::kNone && !spec.requiresGeometryDomain());
-        write_quad(&vb, spec, mode, 1.f, color4f, SkRect::MakeEmpty(), domain, outer);
+        // Else fall through to generic vertex function; this is a spec that has no vertex colors
+        // and [no|uvr] local coords, which doesn't happen often enough to warrant specialization.
     }
 
-    return vb.fPtr;
+    // Arbitrary spec hits the slow path
+    return write_quad_generic;
 }
 
-bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
-                          int quadCount) {
-    if (spec.usesCoverageAA()) {
-        // AA quads use 8 vertices, basically nested rectangles
-        sk_sp<const GrGpuBuffer> ibuffer = get_index_buffer(target->resourceProvider());
-        if (!ibuffer) {
-            return false;
+Tessellator::Tessellator(const VertexSpec& spec, char* vertices)
+        : fVertexSpec(spec)
+        , fVertexWriter{vertices}
+        , fWriteProc(Tessellator::GetWriteQuadProc(spec)) {}
+
+void Tessellator::append(GrQuad* deviceQuad, GrQuad* localQuad,
+                         const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags) {
+    // We allow Tessellator to be created with a null vertices pointer for convenience, but it is
+    // assumed it will never actually be used in those cases.
+    SkASSERT(fVertexWriter.fPtr);
+    SkASSERT(deviceQuad->quadType() <= fVertexSpec.deviceQuadType());
+    SkASSERT(localQuad || !fVertexSpec.hasLocalCoords());
+    SkASSERT(!fVertexSpec.hasLocalCoords() || localQuad->quadType() <= fVertexSpec.localQuadType());
+
+    static const float kFullCoverage[4] = {1.f, 1.f, 1.f, 1.f};
+    static const float kZeroCoverage[4] = {0.f, 0.f, 0.f, 0.f};
+    static const SkRect kIgnoredDomain = SkRect::MakeEmpty();
+
+    if (fVertexSpec.usesCoverageAA()) {
+        SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kWithColor ||
+                 fVertexSpec.coverageMode() == CoverageMode::kWithPosition);
+        // Must calculate inner and outer quadrilaterals for the vertex coverage ramps, and possibly
+        // a geometry domain if corners are not right angles
+        SkRect geomDomain;
+        if (fVertexSpec.requiresGeometryDomain()) {
+            geomDomain = deviceQuad->bounds();
+            geomDomain.outset(0.5f, 0.5f); // account for AA expansion
         }
 
-        mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
-        mesh->setIndexedPatterned(std::move(ibuffer), kIndicesPerAAFillRect, kVertsPerAAFillRect,
-                                  quadCount, kNumAAQuadsInIndexBuffer);
-    } else {
-        // Non-AA quads use 4 vertices, and regular triangle strip layout
-        if (quadCount > 1) {
-            sk_sp<const GrGpuBuffer> ibuffer = target->resourceProvider()->refQuadIndexBuffer();
-            if (!ibuffer) {
-                return false;
+        if (aaFlags == GrQuadAAFlags::kNone) {
+            // Have to write the coverage AA vertex structure, but there's no math to be done for a
+            // non-aa quad batched into a coverage AA op.
+            fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
+                       geomDomain, uvDomain);
+            // Since we pass the same corners in, the outer vertex structure will have 0 area and
+            // the coverage interpolation from 1 to 0 will not be visible.
+            fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
+                       geomDomain, uvDomain);
+        } else {
+            // Reset the tessellation helper to match the current geometry
+            fAAHelper.reset(*deviceQuad, localQuad);
+
+            // Edge inset/outset distance ordered LBTR, set to 0.5 for a half pixel if the AA flag
+            // is turned on, or 0.0 if the edge is not anti-aliased.
+            skvx::Vec<4, float> edgeDistances;
+            if (aaFlags == GrQuadAAFlags::kAll) {
+                edgeDistances = 0.5f;
+            } else {
+                edgeDistances = { (aaFlags & GrQuadAAFlags::kLeft)   ? 0.5f : 0.f,
+                                  (aaFlags & GrQuadAAFlags::kBottom) ? 0.5f : 0.f,
+                                  (aaFlags & GrQuadAAFlags::kTop)    ? 0.5f : 0.f,
+                                  (aaFlags & GrQuadAAFlags::kRight)  ? 0.5f : 0.f };
             }
 
-            mesh->setPrimitiveType(GrPrimitiveType::kTriangles);
-            mesh->setIndexedPatterned(std::move(ibuffer), 6, 4, quadCount,
-                                      GrResourceProvider::QuadCountOfQuadBuffer());
-        } else {
-            mesh->setPrimitiveType(GrPrimitiveType::kTriangleStrip);
-            mesh->setNonIndexedNonInstanced(4);
+            // Write inner vertices first
+            float coverage[4];
+            fAAHelper.inset(edgeDistances, deviceQuad, localQuad).store(coverage);
+            fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, coverage, color,
+                       geomDomain, uvDomain);
+
+            // Then outer vertices, which use 0.f for their coverage
+            fAAHelper.outset(edgeDistances, deviceQuad, localQuad);
+            fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kZeroCoverage, color,
+                       geomDomain, uvDomain);
         }
+    } else {
+        // No outsetting needed, just write a single quad with full coverage
+        SkASSERT(fVertexSpec.coverageMode() == CoverageMode::kNone &&
+                 !fVertexSpec.requiresGeometryDomain());
+        fWriteProc(&fVertexWriter, fVertexSpec, deviceQuad, localQuad, kFullCoverage, color,
+                   kIgnoredDomain, uvDomain);
+    }
+}
+
+sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawOp::Target* target,
+                                     IndexBufferOption indexBufferOption) {
+    auto resourceProvider = target->resourceProvider();
+
+    switch (indexBufferOption) {
+        case IndexBufferOption::kPictureFramed: return resourceProvider->refAAQuadIndexBuffer();
+        case IndexBufferOption::kIndexedRects:  return resourceProvider->refNonAAQuadIndexBuffer();
+        case IndexBufferOption::kTriStrips:     // fall through
+        default:                                return nullptr;
+    }
+}
+
+int QuadLimit(IndexBufferOption option) {
+    switch (option) {
+        case IndexBufferOption::kPictureFramed: return GrResourceProvider::MaxNumAAQuads();
+        case IndexBufferOption::kIndexedRects:  return GrResourceProvider::MaxNumNonAAQuads();
+        case IndexBufferOption::kTriStrips:     return SK_MaxS32; // not limited by an indexBuffer
     }
 
-    return true;
+    SkUNREACHABLE;
+}
+
+void ConfigureMesh(const GrCaps& caps, GrMesh* mesh, const VertexSpec& spec,
+                   int runningQuadCount, int quadsInDraw, int maxVerts,
+                   sk_sp<const GrBuffer> vertexBuffer,
+                   sk_sp<const GrBuffer> indexBuffer, int absVertBufferOffset) {
+    SkASSERT(vertexBuffer);
+
+    mesh->setPrimitiveType(spec.primitiveType());
+
+    if (spec.indexBufferOption() == IndexBufferOption::kTriStrips) {
+        SkASSERT(!indexBuffer);
+
+        mesh->setNonIndexedNonInstanced(4);
+        int offset = absVertBufferOffset +
+                                    runningQuadCount * GrResourceProvider::NumVertsPerNonAAQuad();
+        mesh->setVertexData(std::move(vertexBuffer), offset);
+        return;
+    }
+
+    SkASSERT(spec.indexBufferOption() == IndexBufferOption::kPictureFramed ||
+             spec.indexBufferOption() == IndexBufferOption::kIndexedRects);
+    SkASSERT(indexBuffer);
+
+    int maxNumQuads, numIndicesPerQuad, numVertsPerQuad;
+
+    if (spec.indexBufferOption() == IndexBufferOption::kPictureFramed) {
+        // AA uses 8 vertices and 30 indices per quad, basically nested rectangles
+        maxNumQuads = GrResourceProvider::MaxNumAAQuads();
+        numIndicesPerQuad = GrResourceProvider::NumIndicesPerAAQuad();
+        numVertsPerQuad = GrResourceProvider::NumVertsPerAAQuad();
+    } else {
+        // Non-AA uses 4 vertices and 6 indices per quad
+        maxNumQuads = GrResourceProvider::MaxNumNonAAQuads();
+        numIndicesPerQuad = GrResourceProvider::NumIndicesPerNonAAQuad();
+        numVertsPerQuad = GrResourceProvider::NumVertsPerNonAAQuad();
+    }
+
+    SkASSERT(runningQuadCount + quadsInDraw <= maxNumQuads);
+
+    if (caps.avoidLargeIndexBufferDraws()) {
+        // When we need to avoid large index buffer draws we modify the base vertex of the draw
+        // which, in GL, requires rebinding all vertex attrib arrays, so a base index is generally
+        // preferred.
+        int offset = absVertBufferOffset + runningQuadCount * numVertsPerQuad;
+
+        mesh->setIndexedPatterned(std::move(indexBuffer), numIndicesPerQuad,
+                                  numVertsPerQuad, quadsInDraw, maxNumQuads);
+        mesh->setVertexData(std::move(vertexBuffer), offset);
+    } else {
+        int baseIndex = runningQuadCount * numIndicesPerQuad;
+        int numIndicesToDraw = quadsInDraw * numIndicesPerQuad;
+
+        int minVertex = runningQuadCount * numVertsPerQuad;
+        int maxVertex = (runningQuadCount + quadsInDraw) * numVertsPerQuad;
+
+        mesh->setIndexed(std::move(indexBuffer), numIndicesToDraw,
+                         baseIndex, minVertex, maxVertex, GrPrimitiveRestart::kNo);
+        mesh->setVertexData(std::move(vertexBuffer), absVertBufferOffset);
+    }
 }
 
 ////////////////// VertexSpec Implementation
@@ -727,19 +526,20 @@
 public:
     using Saturate = GrTextureOp::Saturate;
 
-    static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
-        return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, const VertexSpec& spec) {
+        return arena->make<QuadPerEdgeAAGeometryProcessor>(spec);
     }
 
-    static sk_sp<GrGeometryProcessor> Make(const VertexSpec& vertexSpec, const GrShaderCaps& caps,
-                                           const GrBackendFormat& backendFormat,
-                                           const GrSamplerState& samplerState,
-                                           const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                           Saturate saturate) {
-        return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
-                vertexSpec, caps, backendFormat, samplerState, swizzle, extraSamplerKey,
-                std::move(textureColorSpaceXform), saturate));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, const VertexSpec& vertexSpec,
+                                     const GrShaderCaps& caps,
+                                     const GrBackendFormat& backendFormat,
+                                     const GrSamplerState& samplerState,
+                                     const GrSwizzle& swizzle,
+                                     sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                     Saturate saturate) {
+        return arena->make<QuadPerEdgeAAGeometryProcessor>(
+                vertexSpec, caps, backendFormat, samplerState, swizzle,
+                std::move(textureColorSpaceXform), saturate);
     }
 
     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
@@ -775,10 +575,10 @@
         class GLSLProcessor : public GrGLSLGeometryProcessor {
         public:
             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
-                         FPCoordTransformIter&& transformIter) override {
+                         const CoordTransformRange& transformRange) override {
                 const auto& gp = proc.cast<QuadPerEdgeAAGeometryProcessor>();
                 if (gp.fLocalCoord.isInitialized()) {
-                    this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
+                    this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange);
                 }
                 fTextureColorSpaceXformHelper.setData(pdman, gp.fTextureColorSpaceXform.get());
             }
@@ -929,6 +729,8 @@
     }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
     QuadPerEdgeAAGeometryProcessor(const VertexSpec& spec)
             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
             , fTextureColorSpaceXform(nullptr) {
@@ -942,13 +744,12 @@
                                    const GrBackendFormat& backendFormat,
                                    const GrSamplerState& samplerState,
                                    const GrSwizzle& swizzle,
-                                   uint32_t extraSamplerKey,
                                    sk_sp<GrColorSpaceXform> textureColorSpaceXform,
                                    Saturate saturate)
             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
             , fSaturate(saturate)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
-            , fSampler(samplerState, backendFormat, swizzle, extraSamplerKey) {
+            , fSampler(samplerState, backendFormat, swizzle) {
         SkASSERT(spec.hasLocalCoords());
         this->initializeAttrs(spec);
         this->setTextureSamplerCnt(1);
@@ -1024,18 +825,19 @@
     typedef GrGeometryProcessor INHERITED;
 };
 
-sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec) {
-    return QuadPerEdgeAAGeometryProcessor::Make(spec);
+GrGeometryProcessor* MakeProcessor(SkArenaAlloc* arena, const VertexSpec& spec) {
+    return QuadPerEdgeAAGeometryProcessor::Make(arena, spec);
 }
 
-sk_sp<GrGeometryProcessor> MakeTexturedProcessor(const VertexSpec& spec, const GrShaderCaps& caps,
-                                                 const GrBackendFormat& backendFormat,
-                                                 const GrSamplerState& samplerState,
-                                                 const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-                                                 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                                 Saturate saturate) {
-    return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, backendFormat, samplerState, swizzle,
-                                                extraSamplerKey, std::move(textureColorSpaceXform),
+GrGeometryProcessor* MakeTexturedProcessor(SkArenaAlloc* arena, const VertexSpec& spec,
+                                           const GrShaderCaps& caps,
+                                           const GrBackendFormat& backendFormat,
+                                           const GrSamplerState& samplerState,
+                                           const GrSwizzle& swizzle,
+                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                          Saturate saturate) {
+    return QuadPerEdgeAAGeometryProcessor::Make(arena, spec, caps, backendFormat, samplerState,
+                                                swizzle, std::move(textureColorSpaceXform),
                                                 saturate);
 }
 
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index 7cf1f41..35a18fd 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -14,13 +14,16 @@
 #include "src/gpu/GrColor.h"
 #include "src/gpu/GrGeometryProcessor.h"
 #include "src/gpu/GrSamplerState.h"
+#include "src/gpu/GrVertexWriter.h"
 #include "src/gpu/geometry/GrQuad.h"
+#include "src/gpu/geometry/GrQuadUtils.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
 #include "src/gpu/ops/GrTextureOp.h"
 
 class GrCaps;
 class GrColorSpaceXform;
 class GrShaderCaps;
+struct GrVertexWriter;
 
 namespace GrQuadPerEdgeAA {
     using Saturate = GrTextureOp::Saturate;
@@ -30,6 +33,16 @@
     enum class ColorType { kNone, kByte, kHalf, kLast = kHalf };
     static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1;
 
+    enum class IndexBufferOption {
+        kPictureFramed,    // geometrically AA'd   -> 8 verts/quad + an index buffer
+        kIndexedRects,     // non-AA'd but indexed -> 4 verts/quad + an index buffer
+        kTriStrips,        // non-AA'd             -> 4 verts/quad but no index buffer
+        kLast = kTriStrips
+    };
+    static const int kIndexBufferOptionCount = static_cast<int>(IndexBufferOption::kLast) + 1;
+
+    IndexBufferOption CalcIndexBufferOption(GrAAType aa, int numQuads);
+
     // Gets the minimum ColorType that can represent a color.
     ColorType MinColorType(SkPMColor4f, GrClampType, const GrCaps&);
 
@@ -42,17 +55,20 @@
         VertexSpec()
                 : fDeviceQuadType(0)     // kAxisAligned
                 , fLocalQuadType(0)      // kAxisAligned
+                , fIndexBufferOption(0)  // kPictureFramed
                 , fHasLocalCoords(false)
                 , fColorType(0)          // kNone
                 , fHasDomain(false)
                 , fUsesCoverageAA(false)
                 , fCompatibleWithCoverageAsAlpha(false)
-                , fRequiresGeometryDomain(false) { }
+                , fRequiresGeometryDomain(false) {}
 
         VertexSpec(GrQuad::Type deviceQuadType, ColorType colorType, GrQuad::Type localQuadType,
-                   bool hasLocalCoords, Domain domain, GrAAType aa, bool coverageAsAlpha)
+                   bool hasLocalCoords, Domain domain, GrAAType aa, bool coverageAsAlpha,
+                   IndexBufferOption indexBufferOption)
                 : fDeviceQuadType(static_cast<unsigned>(deviceQuadType))
                 , fLocalQuadType(static_cast<unsigned>(localQuadType))
+                , fIndexBufferOption(static_cast<unsigned>(indexBufferOption))
                 , fHasLocalCoords(hasLocalCoords)
                 , fColorType(static_cast<unsigned>(colorType))
                 , fHasDomain(static_cast<unsigned>(domain))
@@ -63,6 +79,9 @@
 
         GrQuad::Type deviceQuadType() const { return static_cast<GrQuad::Type>(fDeviceQuadType); }
         GrQuad::Type localQuadType() const { return static_cast<GrQuad::Type>(fLocalQuadType); }
+        IndexBufferOption indexBufferOption() const {
+            return static_cast<IndexBufferOption>(fIndexBufferOption);
+        }
         bool hasLocalCoords() const { return fHasLocalCoords; }
         ColorType colorType() const { return static_cast<ColorType>(fColorType); }
         bool hasVertexColors() const { return ColorType::kNone != this->colorType(); }
@@ -80,12 +99,27 @@
         CoverageMode coverageMode() const;
         size_t vertexSize() const;
 
+        bool needsIndexBuffer() const { return this->indexBufferOption() !=
+                                               IndexBufferOption::kTriStrips; }
+
+        GrPrimitiveType primitiveType() const {
+            switch (this->indexBufferOption()) {
+                case IndexBufferOption::kPictureFramed: return GrPrimitiveType::kTriangles;
+                case IndexBufferOption::kIndexedRects:  return GrPrimitiveType::kTriangles;
+                case IndexBufferOption::kTriStrips:     return GrPrimitiveType::kTriangleStrip;
+            }
+
+            SkUNREACHABLE;
+        }
+
     private:
         static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type doesn't fit in 2 bits");
         static_assert(kColorTypeCount <= 4, "Color doesn't fit in 2 bits");
+        static_assert(kIndexBufferOptionCount <= 4, "IndexBufferOption doesn't fit in 2 bits");
 
         unsigned fDeviceQuadType: 2;
         unsigned fLocalQuadType: 2;
+        unsigned fIndexBufferOption: 2;
         unsigned fHasLocalCoords: 1;
         unsigned fColorType : 2;
         unsigned fHasDomain: 1;
@@ -96,35 +130,67 @@
         unsigned fRequiresGeometryDomain: 1;
     };
 
-    sk_sp<GrGeometryProcessor> MakeProcessor(const VertexSpec& spec);
+    // A Tessellator is responsible for processing a series of device+local GrQuads into a VBO,
+    // as specified by a VertexSpec. This vertex data can then be processed by a GP created with
+    // MakeProcessor and/or MakeTexturedProcessor.
+    class Tessellator {
+    public:
+        explicit Tessellator(const VertexSpec& spec, char* vertices);
 
-    sk_sp<GrGeometryProcessor> MakeTexturedProcessor(
-            const VertexSpec& spec, const GrShaderCaps& caps, const GrBackendFormat&,
-            const GrSamplerState& samplerState, const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-            sk_sp<GrColorSpaceXform> textureColorSpaceXform, Saturate saturate);
+        // Calculates (as needed) inset and outset geometry for anti-aliasing, and appends all
+        // necessary position and vertex attributes required by this Tessellator's VertexSpec into
+        // the 'vertices' the Tessellator was called with. The insetting and outsetting may
+        // damage the provided GrQuads (as this is intended to work with GrQuadBuffer::Iter).
+        // 'localQuad' can be null if the VertexSpec does not use local coords.
+        void append(GrQuad* deviceQuad, GrQuad* localQuad,
+                    const SkPMColor4f& color, const SkRect& uvDomain, GrQuadAAFlags aaFlags);
 
-    // Fill vertices with the vertex data needed to represent the given quad. The device position,
-    // local coords, vertex color, domain, and edge coefficients will be written and/or computed
-    // based on the configuration in the vertex spec; if that attribute is disabled in the spec,
-    // then its corresponding function argument is ignored.
+        SkDEBUGCODE(char* vertices() const { return (char*) fVertexWriter.fPtr; })
+
+    private:
+        // VertexSpec defines many unique ways to write vertex attributes, which can be handled
+        // generically by branching per-quad based on the VertexSpec. However, there are several
+        // specs that appear in the wild far more frequently, so they use explicit WriteQuadProcs
+        // that have no branches.
+        typedef void (*WriteQuadProc)(GrVertexWriter* vertices, const VertexSpec& spec,
+                                      const GrQuad* deviceQuad, const GrQuad* localQuad,
+                                      const float coverage[4], const SkPMColor4f& color,
+                                      const SkRect& geomDomain, const SkRect& texDomain);
+        static WriteQuadProc GetWriteQuadProc(const VertexSpec& spec);
+
+        GrQuadUtils::TessellationHelper fAAHelper;
+        VertexSpec                      fVertexSpec;
+        GrVertexWriter                  fVertexWriter;
+        WriteQuadProc                   fWriteProc;
+    };
+
+    GrGeometryProcessor* MakeProcessor(SkArenaAlloc*, const VertexSpec&);
+
+    GrGeometryProcessor* MakeTexturedProcessor(
+            SkArenaAlloc*, const VertexSpec&, const GrShaderCaps&, const GrBackendFormat&,
+            const GrSamplerState&, const GrSwizzle&,
+            sk_sp<GrColorSpaceXform> textureColorSpaceXform, Saturate);
+
+    // This method will return the correct index buffer for the specified indexBufferOption.
+    // It will, correctly, return nullptr if the indexBufferOption is kTriStrips.
+    sk_sp<const GrBuffer> GetIndexBuffer(GrMeshDrawOp::Target*, IndexBufferOption);
+
+    // What is the maximum number of quads allowed for the specified indexBuffer option?
+    int QuadLimit(IndexBufferOption);
+
+    // This method will configure the vertex and index data of the provided 'mesh' to comply
+    // with the indexing method specified in the vertexSpec. It is up to the calling code
+    // to allocate and fill in the vertex data and acquire the correct indexBuffer if it is needed.
     //
-    // Tessellation is based on the quad type of the vertex spec, not the provided GrQuad's
-    // so that all quads in a batch are tessellated the same.
-    //
-    // Returns the advanced pointer in vertices.
-    void* Tessellate(void* vertices, const VertexSpec& spec, const GrQuad& deviceQuad,
-                     const SkPMColor4f& color, const GrQuad& localQuad, const SkRect& domain,
-                     GrQuadAAFlags aa);
-
-    // The mesh will have its index data configured to meet the expectations of the Tessellate()
-    // function, but it the calling code must handle filling a vertex buffer via Tessellate() and
-    // then assigning it to the returned mesh.
-    //
-    // Returns false if the index data could not be allocated.
-    bool ConfigureMeshIndices(GrMeshDrawOp::Target* target, GrMesh* mesh, const VertexSpec& spec,
-                              int quadCount);
-
-    static constexpr int kNumAAQuadsInIndexBuffer = 512;
+    // @param runningQuadCount  the number of quads already stored in 'vertexBuffer' and
+    //                          'indexBuffer' e.g., different GrMeshes have already been placed in
+    //                          the buffers to allow dynamic state changes.
+    // @param quadCount         the number of quads that will be drawn by the provided 'mesh'.
+    //                          A subsequent ConfigureMesh call would the use
+    //                          'runningQuadCount' + 'quadCount' for its new 'runningQuadCount'.
+    void ConfigureMesh(const GrCaps&, GrMesh*, const VertexSpec&, int runningQuadCount,
+                       int quadCount, int maxVerts, sk_sp<const GrBuffer> vertexBuffer,
+                       sk_sp<const GrBuffer> indexBuffer, int absVertBufferOffset);
 
 } // namespace GrQuadPerEdgeAA
 
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 6d48a33..cc78057 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -18,16 +18,14 @@
 #include "src/gpu/ops/GrMeshDrawOp.h"
 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
 
-static const int kVertsPerInstance = 4;
-static const int kIndicesPerInstance = 6;
-
-static sk_sp<GrGeometryProcessor> make_gp(const GrShaderCaps* shaderCaps,
-                                          const SkMatrix& viewMatrix,
-                                          bool wideColor) {
+static GrGeometryProcessor* make_gp(SkArenaAlloc* arena,
+                                    const GrShaderCaps* shaderCaps,
+                                    const SkMatrix& viewMatrix,
+                                    bool wideColor) {
     using namespace GrDefaultGeoProcFactory;
-    Color::Type colorType =
-        wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type;
-    return GrDefaultGeoProcFactory::Make(shaderCaps, colorType, Coverage::kSolid_Type,
+    Color::Type colorType = wideColor ? Color::kPremulWideColorAttribute_Type
+                                      : Color::kPremulGrColorAttribute_Type;
+    return GrDefaultGeoProcFactory::Make(arena, shaderCaps, colorType, Coverage::kSolid_Type,
                                          LocalCoords::kUsePosition_Type, viewMatrix);
 }
 
@@ -97,8 +95,8 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp = make_gp(target->caps().shaderCaps(), fViewMatrix,
-                                                fWideColor);
+        GrGeometryProcessor* gp = make_gp(target->allocator(), target->caps().shaderCaps(),
+                                          fViewMatrix, fWideColor);
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
@@ -113,14 +111,9 @@
         if (!numRects) {
             return;
         }
-        sk_sp<const GrGpuBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
-        if (!indexBuffer) {
-            SkDebugf("Could not allocate indices\n");
-            return;
-        }
-        PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
-                             std::move(indexBuffer), kVertsPerInstance, kIndicesPerInstance,
-                             numRects);
+
+        QuadHelper helper(target, gp->vertexStride(), numRects);
+
         GrVertexWriter vertices{helper.vertices()};
         if (!vertices.fPtr) {
             SkDebugf("Could not allocate vertices\n");
@@ -136,7 +129,7 @@
                 iter.next();
             }
         }
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index 546933b..d899954 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -12,6 +12,7 @@
 #include "src/gpu/GrDrawOpTest.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/effects/GrShadowGeoProc.h"
 
@@ -189,8 +190,10 @@
 
     // An insetWidth > 1/2 rect width or height indicates a simple fill.
     ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
-                          float devRadius, bool isCircle, float blurRadius, float insetWidth)
-            : INHERITED(ClassID()) {
+                          float devRadius, bool isCircle, float blurRadius, float insetWidth,
+                          sk_sp<GrTextureProxy> falloffProxy)
+            : INHERITED(ClassID())
+            , fFalloffProxy(falloffProxy) {
         SkRect bounds = devRect;
         SkASSERT(insetWidth > 0);
         SkScalar innerRadius = 0.0f;
@@ -536,7 +539,8 @@
 
     void onPrepareDraws(Target* target) override {
         // Setup geometry processor
-        sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
+        GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(target->allocator(),
+                                                             fFalloffProxy.get());
 
         int instanceCount = fGeoData.count();
         SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
@@ -587,11 +591,14 @@
             }
         }
 
+        auto fixedDynamicState = target->makeFixedDynamicState(1);
+        fixedDynamicState->fPrimitiveProcessorTextures[0] = fFalloffProxy.get();
+
         GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
                          GrPrimitiveRestart::kNo);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, fixedDynamicState, nullptr, GrPrimitiveType::kTriangles);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -607,9 +614,14 @@
         return CombineResult::kMerged;
     }
 
+    void visitProxies(const VisitProxyFunc& func) const override {
+        func(fFalloffProxy.get(), GrMipMapped(false));
+    }
+
     SkSTArray<1, Geometry, true> fGeoData;
     int fVertCount;
     int fIndexCount;
+    sk_sp<GrTextureProxy> fFalloffProxy;
 
     typedef GrMeshDrawOp INHERITED;
 };
@@ -619,6 +631,49 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace GrShadowRRectOp {
+
+static sk_sp<GrTextureProxy> create_falloff_texture(GrProxyProvider* proxyProvider) {
+    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+    GrUniqueKey key;
+    GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
+    builder.finish();
+
+    sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(
+            key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
+    if (!falloffTexture) {
+        static const int kWidth = 128;
+        static const size_t kRowBytes = kWidth*GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
+        SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
+
+        sk_sp<SkData> data = SkData::MakeUninitialized(kRowBytes);
+        if (!data) {
+            return nullptr;
+        }
+        unsigned char* values = (unsigned char*) data->writable_data();
+        for (int i = 0; i < 128; ++i) {
+            SkScalar d = SK_Scalar1 - i/SkIntToScalar(127);
+            values[i] = SkScalarRoundToInt((SkScalarExp(-4*d*d) - 0.018f)*255);
+        }
+
+        sk_sp<SkImage> img = SkImage::MakeRasterData(ii, std::move(data), kRowBytes);
+        if (!img) {
+            return nullptr;
+        }
+
+        falloffTexture = proxyProvider->createTextureProxy(std::move(img), 1, SkBudgeted::kYes,
+                                                           SkBackingFit::kExact);
+        if (!falloffTexture) {
+            return nullptr;
+        }
+
+        SkASSERT(falloffTexture->origin() == kTopLeft_GrSurfaceOrigin);
+        proxyProvider->assignUniqueKeyToProxy(key, falloffTexture.get());
+    }
+
+    return falloffTexture;
+}
+
+
 std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
                                GrColor color,
                                const SkMatrix& viewMatrix,
@@ -628,6 +683,11 @@
     // Shadow rrect ops only handle simple circular rrects.
     SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
 
+    sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context->priv().proxyProvider());
+    if (!falloffTexture) {
+        return nullptr;
+    }
+
     // Do any matrix crunching before we reset the draw state for device coords.
     const SkRect& rrectBounds = rrect.getBounds();
     SkRect bounds;
@@ -649,7 +709,8 @@
                                                  scaledRadius,
                                                  rrect.isOval(),
                                                  blurWidth,
-                                                 scaledInsetWidth);
+                                                 scaledInsetWidth,
+                                                 std::move(falloffTexture));
 }
 }
 
@@ -658,35 +719,42 @@
 #if GR_TEST_UTILS
 
 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
-    // create a similarity matrix
-    SkScalar rotate = random->nextSScalar1() * 360.f;
-    SkScalar translateX = random->nextSScalar1() * 1000.f;
-    SkScalar translateY = random->nextSScalar1() * 1000.f;
-    SkScalar scale;
+    // We may choose matrix and inset values that cause the factory to fail. We loop until we find
+    // an acceptable combination.
     do {
-        scale = random->nextSScalar1() * 100.f;
-    } while (scale == 0);
-    SkMatrix viewMatrix;
-    viewMatrix.setRotate(rotate);
-    viewMatrix.postTranslate(translateX, translateY);
-    viewMatrix.postScale(scale, scale);
-    SkScalar insetWidth = random->nextSScalar1() * 72.f;
-    SkScalar blurWidth = random->nextSScalar1() * 72.f;
-    bool isCircle = random->nextBool();
-    // This op doesn't use a full GrPaint, just a color.
-    GrColor color = paint.getColor4f().toBytes_RGBA();
-    if (isCircle) {
-        SkRect circle = GrTest::TestSquare(random);
-        SkRRect rrect = SkRRect::MakeOval(circle);
-        return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
-    } else {
-        SkRRect rrect;
-        do {
-            // This may return a rrect with elliptical corners, which we don't support.
-            rrect = GrTest::TestRRectSimple(random);
-        } while (!SkRRectPriv::IsSimpleCircular(rrect));
-        return GrShadowRRectOp::Make(context, color, viewMatrix, rrect, blurWidth, insetWidth);
-    }
+        // create a similarity matrix
+        SkScalar rotate = random->nextSScalar1() * 360.f;
+        SkScalar translateX = random->nextSScalar1() * 1000.f;
+        SkScalar translateY = random->nextSScalar1() * 1000.f;
+        SkScalar scale = random->nextSScalar1() * 100.f;
+        SkMatrix viewMatrix;
+        viewMatrix.setRotate(rotate);
+        viewMatrix.postTranslate(translateX, translateY);
+        viewMatrix.postScale(scale, scale);
+        SkScalar insetWidth = random->nextSScalar1() * 72.f;
+        SkScalar blurWidth = random->nextSScalar1() * 72.f;
+        bool isCircle = random->nextBool();
+        // This op doesn't use a full GrPaint, just a color.
+        GrColor color = paint.getColor4f().toBytes_RGBA();
+        if (isCircle) {
+            SkRect circle = GrTest::TestSquare(random);
+            SkRRect rrect = SkRRect::MakeOval(circle);
+            if (auto op = GrShadowRRectOp::Make(
+                    context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
+                return op;
+            }
+        } else {
+            SkRRect rrect;
+            do {
+                // This may return a rrect with elliptical corners, which will cause an assert.
+                rrect = GrTest::TestRRectSimple(random);
+            } while (!SkRRectPriv::IsSimpleCircular(rrect));
+            if (auto op = GrShadowRRectOp::Make(
+                    context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
+                return op;
+            }
+        }
+    } while (true);
 }
 
 #endif
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
index 3c10b8b..9654561 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.cpp
@@ -37,14 +37,9 @@
                                           : GrDrawOp::FixedFunctionFlags::kNone;
 }
 
-static bool none_as_coverage_aa_compatible(GrAAType aa1, GrAAType aa2) {
-    return (aa1 == GrAAType::kNone && aa2 == GrAAType::kCoverage) ||
-           (aa1 == GrAAType::kCoverage && aa2 == GrAAType::kNone);
-}
-
 bool GrSimpleMeshDrawOpHelper::isCompatible(const GrSimpleMeshDrawOpHelper& that,
                                             const GrCaps& caps, const SkRect& thisBounds,
-                                            const SkRect& thatBounds, bool noneAsCoverageAA) const {
+                                            const SkRect& thatBounds, bool ignoreAAType) const {
     if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) {
         return false;
     }
@@ -53,8 +48,18 @@
             return false;
         }
     }
-    bool result = fPipelineFlags == that.fPipelineFlags && (fAAType == that.fAAType ||
-            (noneAsCoverageAA && none_as_coverage_aa_compatible(this->aaType(), that.aaType())));
+
+#ifdef SK_DEBUG
+    if (ignoreAAType) {
+        // If we're ignoring AA it should be bc we already know they are the same or that
+        // the are different but are compatible (i.e., one is AA and the other is None)
+        SkASSERT(fAAType == that.fAAType ||
+                 GrMeshDrawOp::CanUpgradeAAOnMerge(this->aaType(), that.aaType()));
+    }
+#endif
+
+    bool result = fPipelineFlags == that.fPipelineFlags &&
+                  (ignoreAAType || fAAType == that.fAAType);
     SkASSERT(!result || fCompatibleWithCoverageAsAlpha == that.fCompatibleWithCoverageAsAlpha);
     SkASSERT(!result || fUsesLocalCoords == that.fUsesLocalCoords);
     return result;
@@ -176,8 +181,8 @@
 
 bool GrSimpleMeshDrawOpHelperWithStencil::isCompatible(
         const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps& caps,
-        const SkRect& thisBounds, const SkRect& thatBounds, bool noneAsCoverageAA) const {
-    return INHERITED::isCompatible(that, caps, thisBounds, thatBounds, noneAsCoverageAA) &&
+        const SkRect& thisBounds, const SkRect& thatBounds, bool ignoreAAType) const {
+    return INHERITED::isCompatible(that, caps, thisBounds, thatBounds, ignoreAAType) &&
            fStencilSettings == that.fStencilSettings;
 }
 
diff --git a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
index 35bf3cc..f184769 100644
--- a/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
+++ b/src/gpu/ops/GrSimpleMeshDrawOpHelper.h
@@ -54,10 +54,9 @@
 
     GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const;
 
-    // noneAACompatibleWithCoverage should be set to true if the op can properly render a non-AA
-    // primitive merged into a coverage-based op.
+    // ignoreAAType should be set to true if the op already knows the AA settings are acceptible
     bool isCompatible(const GrSimpleMeshDrawOpHelper& that, const GrCaps&, const SkRect& thisBounds,
-                      const SkRect& thatBounds, bool noneAACompatibleWithCoverage = false) const;
+                      const SkRect& thatBounds, bool ignoreAAType = false) const;
 
     /**
      * Finalizes the processor set and determines whether the destination must be provided
@@ -120,7 +119,7 @@
     GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
 
     void setAAType(GrAAType aaType) {
-      fAAType = static_cast<unsigned>(aaType);
+        fAAType = static_cast<unsigned>(aaType);
     }
 
     void executeDrawsAndUploads(const GrOp*, GrOpFlushState*, const SkRect& chainBounds);
@@ -189,7 +188,7 @@
 
     bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps&,
                       const SkRect& thisBounds, const SkRect& thatBounds,
-                      bool noneAACompatibleWithCoverage = false) const;
+                      bool ignoreAAType = false) const;
 
     void executeDrawsAndUploads(const GrOp*, GrOpFlushState*, const SkRect& chainBounds);
 
diff --git a/src/gpu/ops/GrSmallPathRenderer.cpp b/src/gpu/ops/GrSmallPathRenderer.cpp
index b65cb42..742aa58 100644
--- a/src/gpu/ops/GrSmallPathRenderer.cpp
+++ b/src/gpu/ops/GrSmallPathRenderer.cpp
@@ -309,7 +309,7 @@
     struct FlushInfo {
         sk_sp<const GrBuffer> fVertexBuffer;
         sk_sp<const GrBuffer> fIndexBuffer;
-        sk_sp<GrGeometryProcessor>   fGeometryProcessor;
+        GrGeometryProcessor*  fGeometryProcessor;
         GrPipeline::FixedDynamicState* fFixedDynamicState;
         int fVertexOffset;
         int fInstancesToFlush;
@@ -353,7 +353,7 @@
             } else {
                 matrix = &SkMatrix::I();
             }
-            flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
+            flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(target->allocator(),
                     *target->caps().shaderCaps(), *matrix, fWideColor, fAtlas->getProxies(),
                     fAtlas->numActivePages(), GrSamplerState::ClampBilerp(), flags);
         } else {
@@ -364,7 +364,7 @@
                 }
             }
 
-            flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
+            flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(target->allocator(),
                     *target->caps().shaderCaps(), this->color(), fWideColor, fAtlas->getProxies(),
                     fAtlas->numActivePages(), GrSamplerState::ClampNearest(), kA8_GrMaskFormat,
                     invert, false);
@@ -375,14 +375,14 @@
 
         // We need to make sure we don't overflow a 32 bit int when we request space in the
         // makeVertexSpace call below.
-        if (instanceCount > SK_MaxS32 / kVerticesPerQuad) {
+        if (instanceCount > SK_MaxS32 / GrResourceProvider::NumVertsPerNonAAQuad()) {
             return;
         }
-        GrVertexWriter vertices{target->makeVertexSpace(kVertexStride,
-                                                        kVerticesPerQuad * instanceCount,
-                                                        &flushInfo.fVertexBuffer,
-                                                        &flushInfo.fVertexOffset)};
-        flushInfo.fIndexBuffer = target->resourceProvider()->refQuadIndexBuffer();
+        GrVertexWriter vertices{ target->makeVertexSpace(
+            kVertexStride, GrResourceProvider::NumVertsPerNonAAQuad() * instanceCount,
+            &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset)};
+
+        flushInfo.fIndexBuffer = target->resourceProvider()->refNonAAQuadIndexBuffer();
         if (!vertices.fPtr || !flushInfo.fIndexBuffer) {
             SkDebugf("Could not allocate vertices\n");
             return;
@@ -774,7 +774,7 @@
     }
 
     void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
-        GrGeometryProcessor* gp = flushInfo->fGeometryProcessor.get();
+        GrGeometryProcessor* gp = flushInfo->fGeometryProcessor;
         int numAtlasTextures = SkToInt(fAtlas->numActivePages());
         auto proxies = fAtlas->getProxies();
         if (gp->numTextureSamplers() != numAtlasTextures) {
@@ -797,14 +797,16 @@
 
         if (flushInfo->fInstancesToFlush) {
             GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
-            int maxInstancesPerDraw =
-                    static_cast<int>(flushInfo->fIndexBuffer->size() / sizeof(uint16_t) / 6);
-            mesh->setIndexedPatterned(flushInfo->fIndexBuffer, kIndicesPerQuad, kVerticesPerQuad,
-                                      flushInfo->fInstancesToFlush, maxInstancesPerDraw);
+            mesh->setIndexedPatterned(flushInfo->fIndexBuffer,
+                                      GrResourceProvider::NumIndicesPerNonAAQuad(),
+                                      GrResourceProvider::NumVertsPerNonAAQuad(),
+                                      flushInfo->fInstancesToFlush,
+                                      GrResourceProvider::MaxNumNonAAQuads());
             mesh->setVertexData(flushInfo->fVertexBuffer, flushInfo->fVertexOffset);
-            target->recordDraw(
-                    flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fFixedDynamicState, nullptr);
-            flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
+            target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1,
+                               flushInfo->fFixedDynamicState, nullptr, GrPrimitiveType::kTriangles);
+            flushInfo->fVertexOffset += GrResourceProvider::NumVertsPerNonAAQuad() *
+                                        flushInfo->fInstancesToFlush;
             flushInfo->fInstancesToFlush = 0;
         }
     }
diff --git a/src/gpu/ops/GrStencilPathOp.cpp b/src/gpu/ops/GrStencilPathOp.cpp
index 1cfd8ff..18fb37c 100644
--- a/src/gpu/ops/GrStencilPathOp.cpp
+++ b/src/gpu/ops/GrStencilPathOp.cpp
@@ -27,7 +27,7 @@
 }
 
 void GrStencilPathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
-    GrRenderTarget* rt = state->drawOpArgs().renderTarget();
+    GrRenderTarget* rt = state->drawOpArgs().proxy()->peekRenderTarget();
     SkASSERT(rt);
 
     int numStencilBits = rt->renderTargetPriv().numStencilBits();
diff --git a/src/gpu/ops/GrStrokeRectOp.cpp b/src/gpu/ops/GrStrokeRectOp.cpp
index 439a7c7..6c95eae 100644
--- a/src/gpu/ops/GrStrokeRectOp.cpp
+++ b/src/gpu/ops/GrStrokeRectOp.cpp
@@ -176,15 +176,15 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp;
+        GrGeometryProcessor* gp;
         {
             using namespace GrDefaultGeoProcFactory;
             Color color(fColor);
             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
                                                         ? LocalCoords::kUsePosition_Type
                                                         : LocalCoords::kUnused_Type;
-            gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(), color,
-                                               Coverage::kSolid_Type, localCoordsType,
+            gp = GrDefaultGeoProcFactory::Make(target->allocator(), target->caps().shaderCaps(),
+                                               color, Coverage::kSolid_Type, localCoordsType,
                                                fViewMatrix);
         }
 
@@ -224,7 +224,7 @@
         GrMesh* mesh = target->allocMesh(primType);
         mesh->setNonIndexedNonInstanced(vertexCount);
         mesh->setVertexData(std::move(vertexBuffer), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, primType);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -304,11 +304,12 @@
     }
 }
 
-static sk_sp<GrGeometryProcessor> create_aa_stroke_rect_gp(const GrShaderCaps* shaderCaps,
-                                                           bool tweakAlphaForCoverage,
-                                                           const SkMatrix& viewMatrix,
-                                                           bool usesLocalCoords,
-                                                           bool wideColor) {
+static GrGeometryProcessor* create_aa_stroke_rect_gp(SkArenaAlloc* arena,
+                                                     const GrShaderCaps* shaderCaps,
+                                                     bool tweakAlphaForCoverage,
+                                                     const SkMatrix& viewMatrix,
+                                                     bool usesLocalCoords,
+                                                     bool wideColor) {
     using namespace GrDefaultGeoProcFactory;
 
     Coverage::Type coverageType =
@@ -318,7 +319,8 @@
     Color::Type colorType =
         wideColor ? Color::kPremulWideColorAttribute_Type: Color::kPremulGrColorAttribute_Type;
 
-    return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix);
+    return MakeForDeviceSpace(arena, shaderCaps, colorType, coverageType,
+                              localCoordsType, viewMatrix);
 }
 
 class AAStrokeRectOp final : public GrMeshDrawOp {
@@ -469,11 +471,12 @@
 };
 
 void AAStrokeRectOp::onPrepareDraws(Target* target) {
-    sk_sp<GrGeometryProcessor> gp(create_aa_stroke_rect_gp(target->caps().shaderCaps(),
-                                                           fHelper.compatibleWithCoverageAsAlpha(),
-                                                           this->viewMatrix(),
-                                                           fHelper.usesLocalCoords(),
-                                                           fWideColor));
+    GrGeometryProcessor* gp = create_aa_stroke_rect_gp(target->allocator(),
+                                                       target->caps().shaderCaps(),
+                                                       fHelper.compatibleWithCoverageAsAlpha(),
+                                                       this->viewMatrix(),
+                                                       fHelper.usesLocalCoords(),
+                                                       fWideColor);
     if (!gp) {
         SkDebugf("Couldn't create GrGeometryProcessor\n");
         return;
@@ -484,6 +487,7 @@
     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
     int instanceCount = fRects.count();
+    int maxQuads = this->miterStroke() ? kNumMiterRectsInIndexBuffer : kNumBevelRectsInIndexBuffer;
 
     sk_sp<const GrGpuBuffer> indexBuffer =
             GetIndexBuffer(target->resourceProvider(), this->miterStroke());
@@ -493,7 +497,7 @@
     }
     PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
                          std::move(indexBuffer), verticesPerInstance, indicesPerInstance,
-                         instanceCount);
+                         instanceCount, maxQuads);
     GrVertexWriter vertices{ helper.vertices() };
     if (!vertices.fPtr) {
         SkDebugf("Could not allocate vertices\n");
@@ -512,7 +516,7 @@
                                            info.fDegenerate,
                                            fHelper.compatibleWithCoverageAsAlpha());
     }
-    helper.recordDraw(target, std::move(gp));
+    helper.recordDraw(target, gp);
 }
 
 void AAStrokeRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 8ec223d..ba1e853 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -255,7 +255,7 @@
         return path;
     }
 
-    void draw(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
+    void draw(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
         SkASSERT(!fAntiAlias);
         GrResourceProvider* rp = target->resourceProvider();
         bool inverseFill = fShape.inverseFilled();
@@ -279,8 +279,7 @@
         SkScalar tol = GrPathUtils::kDefaultTolerance;
         tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds());
         if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) {
-            this->drawVertices(target, std::move(gp), std::move(cachedVertexBuffer), 0,
-                               actualCount);
+            this->drawVertices(target, gp, std::move(cachedVertexBuffer), 0, actualCount);
             return;
         }
 
@@ -307,10 +306,10 @@
         key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info)));
         rp->assignUniqueKeyToResource(key, vb.get());
 
-        this->drawVertices(target, std::move(gp), std::move(vb), 0, count);
+        this->drawVertices(target, gp, std::move(vb), 0, count);
     }
 
-    void drawAA(Target* target, sk_sp<const GrGeometryProcessor> gp, size_t vertexStride) {
+    void drawAA(Target* target, const GrGeometryProcessor* gp, size_t vertexStride) {
         SkASSERT(fAntiAlias);
         SkPath path = getPath();
         if (path.isEmpty()) {
@@ -326,12 +325,12 @@
         if (count == 0) {
             return;
         }
-        this->drawVertices(target, std::move(gp), allocator.detachVertexBuffer(),
+        this->drawVertices(target, gp, allocator.detachVertexBuffer(),
                            allocator.firstVertex(), count);
     }
 
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp;
+        GrGeometryProcessor* gp;
         {
             using namespace GrDefaultGeoProcFactory;
 
@@ -350,40 +349,43 @@
                 coverageType = Coverage::kSolid_Type;
             }
             if (fAntiAlias) {
-                gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(target->caps().shaderCaps(),
+                gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(target->allocator(),
+                                                                 target->caps().shaderCaps(),
                                                                  color, coverageType,
                                                                  localCoordsType, fViewMatrix);
             } else {
-                gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+                gp = GrDefaultGeoProcFactory::Make(target->allocator(), target->caps().shaderCaps(),
                                                    color, coverageType, localCoordsType,
                                                    fViewMatrix);
             }
         }
-        if (!gp.get()) {
+        if (!gp) {
             return;
         }
         size_t vertexStride = gp->vertexStride();
         if (fAntiAlias) {
-            this->drawAA(target, std::move(gp), vertexStride);
+            this->drawAA(target, gp, vertexStride);
         } else {
-            this->draw(target, std::move(gp), vertexStride);
+            this->draw(target, gp, vertexStride);
         }
     }
 
-    void drawVertices(Target* target, sk_sp<const GrGeometryProcessor> gp, sk_sp<const GrBuffer> vb,
+    void drawVertices(Target* target, const GrGeometryProcessor* gp, sk_sp<const GrBuffer> vb,
                       int firstVertex, int count) {
-        GrMesh* mesh = target->allocMesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines
-                                                               : GrPrimitiveType::kTriangles);
+        GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines
+                                                              : GrPrimitiveType::kTriangles;
+
+        GrMesh* mesh = target->allocMesh(primitiveType);
         mesh->setNonIndexedNonInstanced(count);
         mesh->setVertexData(std::move(vb), firstVertex);
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, primitiveType);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
     }
 
-    Helper fHelper;
+    Helper                  fHelper;
     SkPMColor4f             fColor;
     GrShape                 fShape;
     SkMatrix                fViewMatrix;
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 66ed0fd..b6f5a02 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -91,50 +91,85 @@
     }
 }
 
-// if normalizing the domain then pass 1/width, 1/height, 1 for iw, ih, h. Otherwise pass
-// 1, 1, and height.
-static void compute_domain(Domain domain, GrSamplerState::Filter filter, GrSurfaceOrigin origin,
-                           const SkRect& domainRect, float iw, float ih, float h, SkRect* out) {
+// Describes function for normalizing src coords: [x * iw, y * ih + yOffset] can represent
+// regular and rectangular textures, w/ or w/o origin correction.
+struct NormalizationParams {
+    float fIW; // 1 / width of texture, or 1.0 for texture rectangles
+    float fIH; // 1 / height of texture, or 1.0 for tex rects, X -1 if bottom-left origin
+    float fYOffset; // 0 for top-left origin, height of [normalized] tex if bottom-left
+};
+static NormalizationParams proxy_normalization_params(const GrSurfaceProxyView& proxyView) {
+    // Whether or not the proxy is instantiated, this is the size its texture will be, so we can
+    // normalize the src coordinates up front.
+    SkISize dimensions = proxyView.proxy()->backingStoreDimensions();
+    float iw, ih, h;
+    if (proxyView.proxy()->backendFormat().textureType() == GrTextureType::kRectangle) {
+        iw = ih = 1.f;
+        h = dimensions.height();
+    } else {
+        iw = 1.f / dimensions.width();
+        ih = 1.f / dimensions.height();
+        h = 1.f;
+    }
+
+    if (proxyView.origin() == kBottomLeft_GrSurfaceOrigin) {
+        return {iw, -ih, h};
+    } else {
+        return {iw, ih, 0.0f};
+    }
+}
+
+static void correct_domain_for_bilerp(const NormalizationParams& params,
+                                      SkRect* domainRect) {
+    // Normalized pixel size is also equal to iw and ih, so the insets for bilerp are just
+    // in those units and can be applied safely after normalization. However, if the domain is
+    // smaller than a texel, it should clamp to the center of that axis.
+    float dw = domainRect->width() < params.fIW ? domainRect->width() : params.fIW;
+    float dh = domainRect->height() < params.fIH ? domainRect->height() : params.fIH;
+    domainRect->inset(0.5f * dw, 0.5f * dh);
+}
+
+// Normalize the domain and inset for bilerp as necessary. If 'domainRect' is null, it is assumed
+// no domain constraint is desired, so a sufficiently large rect is returned even if the quad
+// ends up batched with an op that uses domains overall.
+static SkRect normalize_domain(GrSamplerState::Filter filter,
+                               const NormalizationParams& params,
+                               const SkRect* domainRect) {
     static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
-    if (domain == Domain::kNo) {
+    if (!domainRect) {
         // Either the quad has no domain constraint and is batched with a domain constrained op
         // (in which case we want a domain that doesn't restrict normalized tex coords), or the
         // entire op doesn't use the domain, in which case the returned value is ignored.
-        *out = kLargeRect;
-        return;
+        return kLargeRect;
     }
 
-    auto ltrb = Sk4f::Load(&domainRect);
-    if (filter == GrSamplerState::Filter::kBilerp) {
-        auto rblt = SkNx_shuffle<2, 3, 0, 1>(ltrb);
-        auto whwh = (rblt - ltrb).abs();
-        auto c = (rblt + ltrb) * 0.5f;
-        static const Sk4f kOffsets = {0.5f, 0.5f, -0.5f, -0.5f};
-        ltrb = (whwh < 1.f).thenElse(c, ltrb + kOffsets);
-    }
-    ltrb *= Sk4f(iw, ih, iw, ih);
-    if (origin == kBottomLeft_GrSurfaceOrigin) {
-        static const Sk4f kMul = {1.f, -1.f, 1.f, -1.f};
-        const Sk4f kAdd = {0.f, h, 0.f, h};
-        ltrb = SkNx_shuffle<0, 3, 2, 1>(kMul * ltrb + kAdd);
+    auto ltrb = skvx::Vec<4, float>::Load(domainRect);
+    // Normalize and offset
+    ltrb = mad(ltrb, {params.fIW, params.fIH, params.fIW, params.fIH},
+               {0.f, params.fYOffset, 0.f, params.fYOffset});
+    if (params.fIH < 0.f) {
+        // Flip top and bottom to keep the rect sorted when loaded back to SkRect.
+        ltrb = skvx::shuffle<0, 3, 2, 1>(ltrb);
     }
 
-    ltrb.store(out);
+    SkRect out;
+    ltrb.store(&out);
+
+    if (filter != GrSamplerState::Filter::kNearest) {
+        correct_domain_for_bilerp(params, &out);
+    }
+    return out;
 }
 
 // Normalizes logical src coords and corrects for origin
-static void compute_src_quad(GrSurfaceOrigin origin, const GrQuad& srcQuad,
-                               float iw, float ih, float h, GrQuad* out) {
+static void normalize_src_quad(const NormalizationParams& params,
+                               GrQuad* srcQuad) {
     // The src quad should not have any perspective
-    SkASSERT(!srcQuad.hasPerspective() && !out->hasPerspective());
-    skvx::Vec<4, float> xs = srcQuad.x4f() * iw;
-    skvx::Vec<4, float> ys = srcQuad.y4f() * ih;
-    if (origin == kBottomLeft_GrSurfaceOrigin) {
-        ys = h - ys;
-    }
-    xs.store(out->xs());
-    ys.store(out->ys());
-    out->setQuadType(srcQuad.quadType());
+    SkASSERT(!srcQuad->hasPerspective());
+    skvx::Vec<4, float> xs = srcQuad->x4f() * params.fIW;
+    skvx::Vec<4, float> ys = mad(srcQuad->y4f(), params.fIH, params.fYOffset);
+    xs.store(srcQuad->xs());
+    ys.store(srcQuad->ys());
 }
 
 /**
@@ -144,7 +179,7 @@
 class TextureOp final : public GrMeshDrawOp {
 public:
     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                                          sk_sp<GrTextureProxy> proxy,
+                                          GrSurfaceProxyView proxyView,
                                           sk_sp<GrColorSpaceXform> textureXform,
                                           GrSamplerState::Filter filter,
                                           const SkPMColor4f& color,
@@ -155,9 +190,11 @@
                                           const GrQuad& localQuad,
                                           const SkRect* domain) {
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
-        return pool->allocate<TextureOp>(std::move(proxy), std::move(textureXform), filter, color,
-                                         saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
+        return pool->allocate<TextureOp>(std::move(proxyView), std::move(textureXform), filter,
+                                         color, saturate, aaType, aaFlags, deviceQuad, localQuad,
+                                         domain);
     }
+
     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
                                           const GrRenderTargetContext::TextureSetEntry set[],
                                           int cnt,
@@ -167,7 +204,7 @@
                                           SkCanvas::SrcRectConstraint constraint,
                                           const SkMatrix& viewMatrix,
                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
-        size_t size = sizeof(TextureOp) + sizeof(ProxyCountPair) * (cnt - 1);
+        size_t size = sizeof(TextureOp) + sizeof(ViewCountPair) * (cnt - 1);
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
         void* mem = pool->allocate(size);
         return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, saturate, aaType,
@@ -176,8 +213,8 @@
     }
 
     ~TextureOp() override {
-        for (unsigned p = 0; p < fProxyCnt; ++p) {
-            fProxyCountPairs[p].fProxy->unref();
+        for (unsigned p = 1; p < fProxyCnt; ++p) {
+            fViewCountPairs[p].~ViewCountPair();
         }
     }
 
@@ -186,7 +223,7 @@
     void visitProxies(const VisitProxyFunc& func) const override {
         for (unsigned p = 0; p < fProxyCnt; ++p) {
             bool mipped = (GrSamplerState::Filter::kMipMap == this->filter());
-            func(fProxyCountPairs[p].fProxy, GrMipMapped(mipped));
+            func(fViewCountPairs[p].fProxyView.proxy(), GrMipMapped(mipped));
         }
     }
 
@@ -197,21 +234,21 @@
         auto iter = fQuads.iterator();
         for (unsigned p = 0; p < fProxyCnt; ++p) {
             str.appendf("Proxy ID: %d, Filter: %d\n",
-                        fProxyCountPairs[p].fProxy->uniqueID().asUInt(),
+                        fViewCountPairs[p].fProxyView.proxy()->uniqueID().asUInt(),
                         static_cast<int>(fFilter));
             int i = 0;
-            while(i < fProxyCountPairs[p].fQuadCnt && iter.next()) {
-                const GrQuad& quad = iter.deviceQuad();
-                const GrQuad& uv = iter.localQuad();
+            while(i < fViewCountPairs[p].fQuadCnt && iter.next()) {
+                const GrQuad* quad = iter.deviceQuad();
+                GrQuad uv = iter.isLocalValid() ? *(iter.localQuad()) : GrQuad();
                 const ColorDomainAndAA& info = iter.metadata();
                 str.appendf(
                         "%d: Color: 0x%08x, Domain(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
                         "  UVs  [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n"
                         "  Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
-                        i, info.fColor.toBytes_RGBA(), info.fHasDomain, info.fDomainRect.fLeft,
+                        i, info.fColor.toBytes_RGBA(), fDomain, info.fDomainRect.fLeft,
                         info.fDomainRect.fTop, info.fDomainRect.fRight, info.fDomainRect.fBottom,
-                        quad.point(0).fX, quad.point(0).fY, quad.point(1).fX, quad.point(1).fY,
-                        quad.point(2).fX, quad.point(2).fY, quad.point(3).fX, quad.point(3).fY,
+                        quad->point(0).fX, quad->point(0).fY, quad->point(1).fX, quad->point(1).fY,
+                        quad->point(2).fX, quad->point(2).fY, quad->point(3).fX, quad->point(3).fY,
                         uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
                         uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY);
 
@@ -246,24 +283,23 @@
     friend class ::GrOpMemoryPool;
 
     struct ColorDomainAndAA {
-        ColorDomainAndAA(const SkPMColor4f& color, const SkRect* domainRect, GrQuadAAFlags aaFlags)
+        ColorDomainAndAA(const SkPMColor4f& color, const SkRect& domainRect, GrQuadAAFlags aaFlags)
                 : fColor(color)
-                , fDomainRect(domainRect ? *domainRect : SkRect::MakeEmpty())
-                , fHasDomain(static_cast<unsigned>(domainRect ? Domain::kYes : Domain::kNo))
+                , fDomainRect(domainRect)
                 , fAAFlags(static_cast<unsigned>(aaFlags)) {
             SkASSERT(fAAFlags == static_cast<unsigned>(aaFlags));
         }
 
         SkPMColor4f fColor;
+        // If the op doesn't use domains, this is ignored. If the op uses domains and the specific
+        // entry does not, this rect will equal kLargeRect, so it automatically has no effect.
         SkRect fDomainRect;
-        unsigned fHasDomain : 1;
         unsigned fAAFlags : 4;
 
-        Domain domain() const { return Domain(fHasDomain); }
         GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
     };
-    struct ProxyCountPair {
-        GrTextureProxy* fProxy;
+    struct ViewCountPair {
+        GrSurfaceProxyView fProxyView;
         int fQuadCnt;
     };
 
@@ -283,10 +319,8 @@
         GrPipeline::DynamicStateArrays* fDynamicStateArrays = nullptr;
         GrPipeline::FixedDynamicState*  fFixedDynamicState = nullptr;
 
-        // These two member variables are only used by 'onPrePrepareDraws'. The prior five are also
+        // This member variable is only used by 'onPrePrepareDraws'. The prior five are also
         // used by 'onPrepareDraws'
-        // TODO: we could just recompute 'fVertexOffsets' in onPrepareDraws
-        int*                            fVertexOffsets = nullptr;
         char*                           fVertices = nullptr;
 
         // How big should 'fVertices' be to hold all the vertex data?
@@ -294,16 +328,14 @@
             return fNumTotalQuads * fVertexSpec.verticesPerQuad() * fVertexSpec.vertexSize();
         }
 
-#ifdef SK_DEBUG
         int totalNumVertices() const {
             return fNumTotalQuads * fVertexSpec.verticesPerQuad();
         }
-#endif
 
         // Helper to fill in the fFixedDynamicState and fDynamicStateArrays. If there is more
         // than one mesh/proxy they are stored in fDynamicStateArrays but if there is only one
         // it is stored in fFixedDynamicState.
-        void setMeshProxy(int index, GrTextureProxy* proxy) {
+        void setMeshProxy(int index, GrSurfaceProxy* proxy) {
             SkASSERT(index < fNumProxies);
 
             if (fDynamicStateArrays) {
@@ -319,24 +351,6 @@
             }
         }
 
-#ifdef SK_DEBUG
-        GrTextureProxy* getMeshProxy(int index) {
-            SkASSERT(index < fNumProxies);
-
-            if (fDynamicStateArrays) {
-                SkASSERT(fDynamicStateArrays->fPrimitiveProcessorTextures);
-                SkASSERT(fNumProxies > 1);
-
-                return fDynamicStateArrays->fPrimitiveProcessorTextures[index];
-            } else {
-                SkASSERT(fFixedDynamicState);
-                SkASSERT(fNumProxies == 1);
-
-                return fFixedDynamicState->fPrimitiveProcessorTextures[index];
-            }
-        }
-#endif
-
         // Allocate the fields required in both onPrePrepareDraws and onPrepareDraws
         void allocateCommon(SkArenaAlloc* arena, const GrAppliedClip* clip) {
             // We'll use a dynamic state array for the GP textures when there are multiple ops.
@@ -351,7 +365,6 @@
 
         // Allocate the fields only needed by onPrePrepareDraws
         void allocatePrePrepareOnly(SkArenaAlloc* arena) {
-            fVertexOffsets = arena->makeArrayDefault<int>(fNumProxies);
             fVertices = arena->makeArrayDefault<char>(this->totalSizeInBytes());
         }
 
@@ -359,7 +372,7 @@
 
     // dstQuad should be the geometry transformed by the view matrix. If domainRect
     // is not null it will be used to apply the strict src rect constraint.
-    TextureOp(sk_sp<GrTextureProxy> proxy,
+    TextureOp(GrSurfaceProxyView proxyView,
               sk_sp<GrColorSpaceXform> textureColorSpaceXform,
               GrSamplerState::Filter filter,
               const SkPMColor4f& color,
@@ -381,7 +394,8 @@
         fAAType = static_cast<unsigned>(aaType);
 
         // We expect our caller to have already caught this optimization.
-        SkASSERT(!domainRect || !domainRect->contains(proxy->backingStoreBoundsRect()));
+        SkASSERT(!domainRect ||
+                 !domainRect->contains(proxyView.proxy()->backingStoreBoundsRect()));
 
         // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
         // If we don't have (or determined we don't need) coverage AA then we can skip using a
@@ -391,14 +405,22 @@
             domainRect = nullptr;
         }
 
-        fQuads.append(dstQuad, {color, domainRect, aaFlags}, &srcQuad);
+        // Normalize src coordinates and the domain (if set)
+        NormalizationParams params = proxy_normalization_params(proxyView);
+        GrQuad normalizedSrcQuad = srcQuad;
+        normalize_src_quad(params, &normalizedSrcQuad);
+        SkRect domain = normalize_domain(filter, params, domainRect);
+
+        fQuads.append(dstQuad, {color, domain, aaFlags}, &normalizedSrcQuad);
 
         fProxyCnt = 1;
-        fProxyCountPairs[0] = {proxy.release(), 1};
+        fViewCountPairs[0] = {std::move(proxyView), 1};
+        fTotNumQuads = 1;
         this->setBounds(dstQuad.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
                         IsHairline::kNo);
         fDomain = static_cast<unsigned>(domainRect != nullptr);
     }
+
     TextureOp(const GrRenderTargetContext::TextureSetEntry set[],
               int cnt,
               GrSamplerState::Filter filter,
@@ -411,20 +433,37 @@
             , fQuads(cnt, true /* includes locals */)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
             , fPrePreparedDesc(nullptr)
-            , fSaturate(static_cast<unsigned>(saturate))
-            , fFilter(static_cast<unsigned>(filter)) {
+            , fSaturate(static_cast<unsigned>(saturate)) {
         fProxyCnt = SkToUInt(cnt);
         SkRect bounds = SkRectPriv::MakeLargestInverted();
-        GrAAType overallAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
-        bool mustFilter = false;
-        bool allOpaque = true;
+
+        GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
         Domain netDomain = Domain::kNo;
-        GrTextureProxy* curProxy = nullptr;
+        GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
+
+        // Net domain and filter quality are being determined simultaneously while iterating through
+        // the entry set. When filter changes to bilerp, all prior normalized domains in the
+        // GrQuadBuffer must be updated to reflect the 1/2px inset required. All quads appended
+        // afterwards will properly take that into account.
+        int correctDomainUpToIndex = 0;
+        const GrSurfaceProxy* curProxy;
+
         for (unsigned p = 0; p < fProxyCnt; ++p) {
-            fProxyCountPairs[p].fProxy = curProxy = SkRef(set[p].fProxy.get());
-            fProxyCountPairs[p].fQuadCnt = 1;
-            SkASSERT(curProxy->textureType() == fProxyCountPairs[0].fProxy->textureType());
-            SkASSERT(curProxy->config() == fProxyCountPairs[0].fProxy->config());
+            if (p == 0) {
+                // We do not placement new the first ViewCountPair since that one is allocated and
+                // initialized as part of the GrTextureOp creation.
+                fViewCountPairs[p].fProxyView = std::move(set[p].fProxyView);
+                fViewCountPairs[p].fQuadCnt = 1;
+            } else {
+                // We must placement new the ViewCountPairs here so that the sk_sps in the
+                // GrSurfaceProxyView get initialized properly.
+                new(&fViewCountPairs[p])ViewCountPair({std::move(set[p].fProxyView), 1});
+            }
+            fTotNumQuads += 1;
+            curProxy = fViewCountPairs[p].fProxyView.proxy();
+            SkASSERT(curProxy->backendFormat().textureType() ==
+                     fViewCountPairs[0].fProxyView.proxy()->backendFormat().textureType());
+            SkASSERT(curProxy->config() == fViewCountPairs[0].fProxyView.proxy()->config());
 
             SkMatrix ctm = viewMatrix;
             if (set[p].fPreViewMatrix) {
@@ -445,19 +484,34 @@
                 srcQuad = GrQuad(set[p].fSrcRect);
             }
 
-            if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) {
-                mustFilter = filter_has_effect(srcQuad, quad);
+            // Before normalizing the source coordinates, determine if bilerp is actually needed
+            if (netFilter != filter && filter_has_effect(srcQuad, quad)) {
+                // The only way netFilter != filter is if bilerp is requested and we haven't yet
+                // found a quad that requires bilerp (so net is still nearest).
+                SkASSERT(netFilter == GrSamplerState::Filter::kNearest &&
+                         filter == GrSamplerState::Filter::kBilerp);
+                netFilter = GrSamplerState::Filter::kBilerp;
+                // All quads index < p with domains were calculated as if there was no filtering,
+                // which is no longer true.
+                correctDomainUpToIndex = p;
             }
 
+            // Normalize the src quads and apply origin
+            NormalizationParams proxyParams =
+                    proxy_normalization_params(fViewCountPairs[p].fProxyView);
+            normalize_src_quad(proxyParams, &srcQuad);
+
+            // Update overall bounds of the op as the union of all quads
             bounds.joinPossiblyEmptyRect(quad.bounds());
+
+            // Determine the AA type for the quad, then merge with net AA type
             GrQuadAAFlags aaFlags;
-            // Don't update the overall aaType, might be inappropriate for some of the quads
             GrAAType aaForQuad;
             GrQuadUtils::ResolveAAType(aaType, set[p].fAAFlags, quad, &aaForQuad, &aaFlags);
             // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
             SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
-            if (overallAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
-                overallAAType = aaType;
+            if (netAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
+                netAAType = aaType;
             }
 
             // Calculate metadata for the entry
@@ -465,133 +519,138 @@
             if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
                 // Check (briefly) if the strict constraint is needed for this set entry
                 if (!set[p].fSrcRect.contains(curProxy->backingStoreBoundsRect()) &&
-                    (mustFilter || aaForQuad == GrAAType::kCoverage)) {
+                    (netFilter == GrSamplerState::Filter::kBilerp ||
+                     aaForQuad == GrAAType::kCoverage)) {
                     // Can't rely on hardware clamping and the draw will access outer texels
-                    // for AA and/or bilerp
+                    // for AA and/or bilerp. Unlike filter quality, this op still has per-quad
+                    // control over AA so that can check aaForQuad, not netAAType.
                     netDomain = Domain::kYes;
                     domainForQuad = &set[p].fSrcRect;
                 }
             }
+
+            SkRect domain = normalize_domain(filter, proxyParams, domainForQuad);
             float alpha = SkTPin(set[p].fAlpha, 0.f, 1.f);
-            allOpaque &= (1.f == alpha);
-            SkPMColor4f color{alpha, alpha, alpha, alpha};
-            fQuads.append(quad, {color, domainForQuad, aaFlags}, &srcQuad);
+            fQuads.append(quad, {{alpha, alpha, alpha, alpha}, domain, aaFlags}, &srcQuad);
         }
-        fAAType = static_cast<unsigned>(overallAAType);
-        if (!mustFilter) {
-            fFilter = static_cast<unsigned>(GrSamplerState::Filter::kNearest);
+
+        // All the quads have been recorded, but some domains need to be fixed
+        if (netDomain == Domain::kYes && correctDomainUpToIndex > 0) {
+            int p = 0;
+            auto iter = fQuads.metadata();
+            while(p < correctDomainUpToIndex && iter.next()) {
+                NormalizationParams proxyParams =
+                    proxy_normalization_params(fViewCountPairs[p].fProxyView);
+                correct_domain_for_bilerp(proxyParams, &(iter->fDomainRect));
+                p++;
+            }
         }
-        this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage),
-                        IsHairline::kNo);
+
+        fAAType = static_cast<unsigned>(netAAType);
+        fFilter = static_cast<unsigned>(netFilter);
         fDomain = static_cast<unsigned>(netDomain);
+
+        this->setBounds(bounds, HasAABloat(netAAType == GrAAType::kCoverage), IsHairline::kNo);
     }
 
-    static void Tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
-                     GrQuadBuffer<ColorDomainAndAA>::Iter* iter, int cnt,
-                     GrSamplerState::Filter filter) {
-        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
-        auto origin = proxy->origin();
-        SkISize dimensions = proxy->backingStoreDimensions();
-
-        float iw, ih, h;
-        if (proxy->textureType() == GrTextureType::kRectangle) {
-            iw = ih = 1.f;
-            h = dimensions.height();
-        } else {
-            iw = 1.f / dimensions.width();
-            ih = 1.f / dimensions.height();
-            h = 1.f;
-        }
-
-        int i = 0;
-        // Explicit ctor ensures ws are 1s, which compute_src_quad requires
-        GrQuad srcQuad(SkRect::MakeEmpty());
-        SkRect domain;
-        while(i < cnt && iter->next()) {
-            SkASSERT(iter->isLocalValid());
-            const ColorDomainAndAA& info = iter->metadata();
-            // Must correct the texture coordinates and domain now that the real texture size
-            // is known
-            compute_src_quad(origin, iter->localQuad(), iw, ih, h, &srcQuad);
-            compute_domain(info.domain(), filter, origin, info.fDomainRect, iw, ih, h,
-                           &domain);
-            v = GrQuadPerEdgeAA::Tessellate(v, spec, iter->deviceQuad(), info.fColor, srcQuad,
-                                            domain, info.aaFlags());
-            i++;
-        }
-    }
-
-    void onPrePrepareDraws(GrRecordingContext* context, const GrAppliedClip* clip) override {
+    void onPrePrepareDraws(GrRecordingContext* context,
+                           const GrSurfaceProxyView* dstView,
+                           GrAppliedClip* clip,
+                           const GrXferProcessor::DstProxyView& dstProxyView) override {
         TRACE_EVENT0("skia.gpu", TRACE_FUNC);
 
         SkDEBUGCODE(this->validate();)
         SkASSERT(!fPrePreparedDesc);
 
-        SkArenaAlloc* arena = context->priv().opPODAllocator();
+        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
 
         fPrePreparedDesc = arena->make<PrePreparedDesc>();
 
-        fPrePreparedDesc->fVertexSpec = this->characterize(&fPrePreparedDesc->fNumProxies,
-                                                           &fPrePreparedDesc->fNumTotalQuads);
+        this->characterize(fPrePreparedDesc);
+
         fPrePreparedDesc->allocateCommon(arena, clip);
 
         fPrePreparedDesc->allocatePrePrepareOnly(arena);
 
-        {
-            SkDEBUGCODE(int totQuadsSeen = 0;)
-            SkDEBUGCODE(int totVerticesSeen = 0;)
-            int vertexOffsetInBuffer = 0;
-            char* dst = fPrePreparedDesc->fVertices;
-            const size_t vertexSize = fPrePreparedDesc->fVertexSpec.vertexSize();
+        // At this juncture we only fill in the vertex data and state arrays. Filling in of
+        // the meshes is left until onPrepareDraws.
+        SkAssertResult(FillInData(*context->priv().caps(), this, fPrePreparedDesc,
+                                  fPrePreparedDesc->fVertices, nullptr, 0, nullptr, nullptr));
+    }
 
-            int meshIndex = 0;
-            for (const auto& op : ChainRange<TextureOp>(this)) {
-                auto iter = op.fQuads.iterator();
-                for (unsigned p = 0; p < op.fProxyCnt; ++p) {
-                    GrTextureProxy* proxy = op.fProxyCountPairs[p].fProxy;
+    static bool FillInData(const GrCaps& caps, TextureOp* texOp, PrePreparedDesc* desc,
+                           char* pVertexData, GrMesh* meshes, int absBufferOffset,
+                           sk_sp<const GrBuffer> vertexBuffer,
+                           sk_sp<const GrBuffer> indexBuffer) {
+        int totQuadsSeen = 0;
+        SkDEBUGCODE(int totVerticesSeen = 0;)
+        SkDEBUGCODE(const size_t vertexSize = desc->fVertexSpec.vertexSize());
 
-                    int quadCnt = op.fProxyCountPairs[p].fQuadCnt;
-                    SkDEBUGCODE(totQuadsSeen += quadCnt;)
+        GrQuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec, pVertexData);
+        int meshIndex = 0;
+        for (const auto& op : ChainRange<TextureOp>(texOp)) {
+            auto iter = op.fQuads.iterator();
+            for (unsigned p = 0; p < op.fProxyCnt; ++p) {
+                GrSurfaceProxy* proxy = op.fViewCountPairs[p].fProxyView.proxy();
 
-                    int meshVertexCnt = quadCnt * fPrePreparedDesc->fVertexSpec.verticesPerQuad();
-                    SkDEBUGCODE(totVerticesSeen += meshVertexCnt);
+                const int quadCnt = op.fViewCountPairs[p].fQuadCnt;
+                SkDEBUGCODE(int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad());
+                SkASSERT(meshIndex < desc->fNumProxies);
 
-                    Tess(dst, fPrePreparedDesc->fVertexSpec, proxy, &iter, quadCnt, op.filter());
+                if (pVertexData) {
+                    for (int i = 0; i < quadCnt && iter.next(); ++i) {
+                        SkASSERT(iter.isLocalValid());
+                        const ColorDomainAndAA& info = iter.metadata();
+                        tessellator.append(iter.deviceQuad(), iter.localQuad(),
+                                           info.fColor, info.fDomainRect, info.aaFlags());
+                    }
+                    desc->setMeshProxy(meshIndex, proxy);
 
-                    fPrePreparedDesc->fVertexOffsets[meshIndex] = vertexOffsetInBuffer;
-                    SkASSERT(vertexOffsetInBuffer * vertexSize ==
-                             (size_t)(dst - fPrePreparedDesc->fVertices));
-                    fPrePreparedDesc->setMeshProxy(meshIndex, proxy);
-                    ++meshIndex;
-
-                    vertexOffsetInBuffer += meshVertexCnt;
-                    dst += vertexSize * meshVertexCnt;
+                    SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize
+                             == (size_t)(tessellator.vertices() - pVertexData));
                 }
-                // If quad counts per proxy were calculated correctly, the entire iterator
-                // should have been consumed.
-                SkASSERT(!iter.next());
+
+                if (meshes) {
+                    GrQuadPerEdgeAA::ConfigureMesh(caps, &(meshes[meshIndex]), desc->fVertexSpec,
+                                                   totQuadsSeen, quadCnt, desc->totalNumVertices(),
+                                                   vertexBuffer, indexBuffer, absBufferOffset);
+                }
+
+                ++meshIndex;
+
+                totQuadsSeen += quadCnt;
+                SkDEBUGCODE(totVerticesSeen += meshVertexCnt);
+                SkASSERT(totQuadsSeen * desc->fVertexSpec.verticesPerQuad() == totVerticesSeen);
             }
 
-            SkASSERT(fPrePreparedDesc->totalSizeInBytes() ==
-                                                     (size_t)(dst - fPrePreparedDesc->fVertices));
-            SkASSERT(meshIndex == fPrePreparedDesc->fNumProxies);
-            SkASSERT(totQuadsSeen == fPrePreparedDesc->fNumTotalQuads);
-            SkASSERT(totVerticesSeen == fPrePreparedDesc->totalNumVertices());
+            // If quad counts per proxy were calculated correctly, the entire iterator
+            // should have been consumed.
+            SkASSERT(!pVertexData || !iter.next());
         }
+
+        SkASSERT(!pVertexData ||
+                 (desc->totalSizeInBytes() == (size_t)(tessellator.vertices() - pVertexData)));
+        SkASSERT(meshIndex == desc->fNumProxies);
+        SkASSERT(totQuadsSeen == desc->fNumTotalQuads);
+        SkASSERT(totVerticesSeen == desc->totalNumVertices());
+        return true;
     }
 
 #ifdef SK_DEBUG
     void validate() const override {
-        auto textureType = fProxyCountPairs[0].fProxy->textureType();
-        const GrSwizzle& swizzle = fProxyCountPairs[0].fProxy->textureSwizzle();
+        // NOTE: Since this is debug-only code, we use the virtual asTextureProxy()
+        auto textureType = fViewCountPairs[0].fProxyView.asTextureProxy()->textureType();
         GrAAType aaType = this->aaType();
 
+        int quadCount = 0;
         for (const auto& op : ChainRange<TextureOp>(this)) {
             for (unsigned p = 0; p < op.fProxyCnt; ++p) {
-                auto* proxy = op.fProxyCountPairs[p].fProxy;
+                auto* proxy = op.fViewCountPairs[p].fProxyView.asTextureProxy();
+                quadCount += op.fViewCountPairs[p].fQuadCnt;
                 SkASSERT(proxy);
                 SkASSERT(proxy->textureType() == textureType);
-                SkASSERT(proxy->textureSwizzle() == swizzle);
+                SkASSERT(op.fViewCountPairs[p].fProxyView.swizzle() ==
+                         fViewCountPairs[0].fProxyView.swizzle());
             }
 
             // Each individual op must be a single aaType. kCoverage and kNone ops can chain
@@ -602,18 +661,25 @@
                 SkASSERT(aaType == GrAAType::kMSAA && op.aaType() == GrAAType::kMSAA);
             }
         }
+
+        SkASSERT(quadCount == this->numChainedQuads());
     }
 #endif
 
-    VertexSpec characterize(int* numProxies, int* numTotalQuads) const {
+#if GR_TEST_UTILS
+    int numQuads() const final { return this->totNumQuads(); }
+#endif
+
+    void characterize(PrePreparedDesc* desc) const {
         GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
         ColorType colorType = ColorType::kNone;
         GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
         Domain domain = Domain::kNo;
         GrAAType overallAAType = this->aaType();
 
-        *numProxies = 0;
-        *numTotalQuads = 0;
+        desc->fNumProxies = 0;
+        desc->fNumTotalQuads = 0;
+        int maxQuadsPerMesh = 0;
 
         for (const auto& op : ChainRange<TextureOp>(this)) {
             if (op.fQuads.deviceQuadType() > quadType) {
@@ -626,17 +692,56 @@
                 domain = Domain::kYes;
             }
             colorType = SkTMax(colorType, static_cast<ColorType>(op.fColorType));
-            *numProxies += op.fProxyCnt;
+            desc->fNumProxies += op.fProxyCnt;
+
             for (unsigned p = 0; p < op.fProxyCnt; ++p) {
-                *numTotalQuads += op.fProxyCountPairs[p].fQuadCnt;
+                maxQuadsPerMesh = SkTMax(op.fViewCountPairs[p].fQuadCnt, maxQuadsPerMesh);
             }
+            desc->fNumTotalQuads += op.totNumQuads();
+
             if (op.aaType() == GrAAType::kCoverage) {
                 overallAAType = GrAAType::kCoverage;
             }
         }
 
-        return VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true, domain,
-                          overallAAType, /* alpha as coverage */ true);
+        SkASSERT(desc->fNumTotalQuads == this->numChainedQuads());
+
+        SkASSERT(!CombinedQuadCountWillOverflow(overallAAType, false, desc->fNumTotalQuads));
+
+        auto indexBufferOption = GrQuadPerEdgeAA::CalcIndexBufferOption(overallAAType,
+                                                                        maxQuadsPerMesh);
+
+        desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true,
+                                       domain, overallAAType, /* alpha as coverage */ true,
+                                       indexBufferOption);
+
+        SkASSERT(desc->fNumTotalQuads <= GrQuadPerEdgeAA::QuadLimit(indexBufferOption));
+    }
+
+    int totNumQuads() const {
+#ifdef SK_DEBUG
+        int tmp = 0;
+        for (unsigned p = 0; p < fProxyCnt; ++p) {
+            tmp += fViewCountPairs[p].fQuadCnt;
+        }
+        SkASSERT(tmp == fTotNumQuads);
+#endif
+
+        return fTotNumQuads;
+    }
+
+    int numChainedQuads() const {
+        int numChainedQuads = this->totNumQuads();
+
+        for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) {
+            numChainedQuads += ((const TextureOp*)tmp)->totNumQuads();
+        }
+
+        for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) {
+            numChainedQuads += ((const TextureOp*)tmp)->totNumQuads();
+        }
+
+        return numChainedQuads;
     }
 
     // onPrePrepareDraws may or may not have been called at this point
@@ -652,99 +757,76 @@
         } else {
             SkArenaAlloc* arena = target->allocator();
 
-            desc.fVertexSpec = this->characterize(&desc.fNumProxies, &desc.fNumTotalQuads);
+            this->characterize(&desc);
             desc.allocateCommon(arena, target->appliedClip());
 
-            SkASSERT(!desc.fVertexOffsets && !desc.fVertices);
+            SkASSERT(!desc.fVertices);
         }
 
         size_t vertexSize = desc.fVertexSpec.vertexSize();
 
-        GrMesh* meshes = target->allocMeshes(desc.fNumProxies);
         sk_sp<const GrBuffer> vbuffer;
         int vertexOffsetInBuffer = 0;
-        int numQuadVerticesLeft = desc.fNumTotalQuads * desc.fVertexSpec.verticesPerQuad();
-        int numAllocatedVertices = 0;
-        void* vdata = nullptr;
 
-        int meshIndex = 0;
-        for (const auto& op : ChainRange<TextureOp>(this)) {
-            auto iter = op.fQuads.iterator();
-            for (unsigned p = 0; p < op.fProxyCnt; ++p) {
-                int quadCnt = op.fProxyCountPairs[p].fQuadCnt;
-                auto* proxy = op.fProxyCountPairs[p].fProxy;
-                int meshVertexCnt = quadCnt * desc.fVertexSpec.verticesPerQuad();
-                if (numAllocatedVertices < meshVertexCnt) {
-                    vdata = target->makeVertexSpaceAtLeast(
-                            vertexSize, meshVertexCnt, numQuadVerticesLeft, &vbuffer,
-                            &vertexOffsetInBuffer, &numAllocatedVertices);
-                    SkASSERT(numAllocatedVertices <= numQuadVerticesLeft);
-                    if (!vdata) {
-                        SkDebugf("Could not allocate vertices\n");
-                        return;
-                    }
-                }
-                SkASSERT(numAllocatedVertices >= meshVertexCnt);
-
-                if (fPrePreparedDesc) {
-                    // TODO: when we've prePrepared the vertex data should we just allocate
-                    // all the vertices together and just do one memcpy?
-                    size_t offset = desc.fVertexOffsets[meshIndex] * vertexSize;
-                    memcpy(vdata, &desc.fVertices[offset], meshVertexCnt * vertexSize);
-                    SkASSERT(proxy == desc.getMeshProxy(meshIndex));
-                } else {
-                    Tess(vdata, desc.fVertexSpec, proxy, &iter, quadCnt, op.filter());
-                    desc.setMeshProxy(meshIndex, proxy);
-                }
-
-                SkASSERT(meshIndex < desc.fNumProxies);
-
-                if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, &(meshes[meshIndex]),
-                                                           desc.fVertexSpec, quadCnt)) {
-                    SkDebugf("Could not allocate indices");
-                    return;
-                }
-                meshes[meshIndex].setVertexData(vbuffer, vertexOffsetInBuffer);
-                ++meshIndex;
-
-                numAllocatedVertices -= meshVertexCnt;
-                numQuadVerticesLeft -= meshVertexCnt;
-                vertexOffsetInBuffer += meshVertexCnt;
-                vdata = reinterpret_cast<char*>(vdata) + vertexSize * meshVertexCnt;
-            }
-
-            // If quad counts per proxy were calculated correctly, the entire iterator should
-            // have been consumed.
-            SkASSERT(fPrePreparedDesc || !iter.next());
+        void* vdata = target->makeVertexSpace(vertexSize, desc.totalNumVertices(),
+                                              &vbuffer, &vertexOffsetInBuffer);
+        if (!vdata) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
         }
-        SkASSERT(!numQuadVerticesLeft);
-        SkASSERT(!numAllocatedVertices);
 
-        sk_sp<GrGeometryProcessor> gp;
+        sk_sp<const GrBuffer> indexBuffer;
+        if (desc.fVertexSpec.needsIndexBuffer()) {
+            indexBuffer = GrQuadPerEdgeAA::GetIndexBuffer(target,
+                                                          desc.fVertexSpec.indexBufferOption());
+            if (!indexBuffer) {
+                SkDebugf("Could not allocate indices\n");
+                return;
+            }
+        }
+
+        // Note: this allocation is always in the flush-time arena (i.e., the flushState)
+        GrMesh* meshes = target->allocMeshes(desc.fNumProxies);
+
+        bool result;
+        if (fPrePreparedDesc) {
+            memcpy(vdata, desc.fVertices, desc.totalSizeInBytes());
+            // The above memcpy filled in the vertex data - just call FillInData to fill in the
+            // mesh data
+            result = FillInData(target->caps(), this, &desc, nullptr, meshes, vertexOffsetInBuffer,
+                                std::move(vbuffer), std::move(indexBuffer));
+        } else {
+            // Fills in both vertex data and mesh data
+            result = FillInData(target->caps(), this, &desc, (char*) vdata, meshes,
+                                vertexOffsetInBuffer, std::move(vbuffer), std::move(indexBuffer));
+        }
+
+        if (!result) {
+            return;
+        }
+
+        GrGeometryProcessor* gp;
 
         {
-            const GrBackendFormat& backendFormat = fProxyCountPairs[0].fProxy->backendFormat();
-            const GrSwizzle& swizzle = fProxyCountPairs[0].fProxy->textureSwizzle();
+            const GrBackendFormat& backendFormat =
+                    fViewCountPairs[0].fProxyView.proxy()->backendFormat();
+            const GrSwizzle& swizzle = fViewCountPairs[0].fProxyView.swizzle();
 
             GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
                                                          this->filter());
 
             auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
 
-            GrGpu* gpu = target->resourceProvider()->priv().gpu();
-            uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(samplerState,
-                                                                         backendFormat);
-
-            gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
+            gp = GrQuadPerEdgeAA::MakeTexturedProcessor(target->allocator(),
                 desc.fVertexSpec, *target->caps().shaderCaps(), backendFormat,
-                samplerState, swizzle, extraSamplerKey, std::move(fTextureColorSpaceXform),
-                saturate);
+                samplerState, swizzle, std::move(fTextureColorSpaceXform), saturate);
 
             SkASSERT(vertexSize == gp->vertexStride());
         }
 
-        target->recordDraw(std::move(gp), meshes, desc.fNumProxies,
-                           desc.fFixedDynamicState, desc.fDynamicStateArrays);
+        target->recordDraw(gp, meshes, desc.fNumProxies,
+                           desc.fFixedDynamicState, desc.fDynamicStateArrays,
+                           desc.fVertexSpec.primitiveType());
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -775,27 +857,36 @@
                                        that->fTextureColorSpaceXform.get())) {
             return CombineResult::kCannotCombine;
         }
+
         bool upgradeToCoverageAAOnMerge = false;
         if (this->aaType() != that->aaType()) {
-            if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) ||
-                  (that->aaType() == GrAAType::kCoverage && this->aaType() == GrAAType::kNone))) {
+            if (!CanUpgradeAAOnMerge(this->aaType(), that->aaType())) {
                 return CombineResult::kCannotCombine;
             }
             upgradeToCoverageAAOnMerge = true;
         }
+
+        if (CombinedQuadCountWillOverflow(this->aaType(), upgradeToCoverageAAOnMerge,
+                                          this->numChainedQuads() + that->numChainedQuads())) {
+            return CombineResult::kCannotCombine;
+        }
+
         if (fSaturate != that->fSaturate) {
             return CombineResult::kCannotCombine;
         }
         if (fFilter != that->fFilter) {
             return CombineResult::kCannotCombine;
         }
-        auto thisProxy = fProxyCountPairs[0].fProxy;
-        auto thatProxy = that->fProxyCountPairs[0].fProxy;
-        if (fProxyCnt > 1 || that->fProxyCnt > 1 ||
-            thisProxy->uniqueID() != thatProxy->uniqueID()) {
+        const auto& thisView = fViewCountPairs[0].fProxyView;
+        const auto& thatView = that->fViewCountPairs[0].fProxyView;
+        auto thisProxy = thisView.proxy();
+        auto thatProxy = thatView.proxy();
+        if (fProxyCnt > 1 || that->fProxyCnt > 1 || thisView != thatView) {
             // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
             if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
-                caps.dynamicStateArrayGeometryProcessorTextureSupport()) {
+                caps.dynamicStateArrayGeometryProcessorTextureSupport() &&
+                thisView.swizzle() == thatView.swizzle() &&
+                thisView.origin() == thatView.origin()) {
                 return CombineResult::kMayChain;
             }
             return CombineResult::kCannotCombine;
@@ -809,7 +900,8 @@
 
         // Concatenate quad lists together
         fQuads.concat(that->fQuads);
-        fProxyCountPairs[0].fQuadCnt += that->fQuads.count();
+        fViewCountPairs[0].fQuadCnt += that->fQuads.count();
+        fTotNumQuads += that->fQuads.count();
 
         return CombineResult::kMerged;
     }
@@ -823,6 +915,7 @@
     // it - and the matching dynamic and fixed state - have been allocated in the opPOD arena
     // not in the FlushState arena.
     PrePreparedDesc* fPrePreparedDesc;
+    int fTotNumQuads = 0;   // the total number of quads in this op (but not in the whole chain)
     unsigned fSaturate : 1;
     unsigned fFilter : 2;
     unsigned fAAType : 2;
@@ -832,9 +925,9 @@
     unsigned fProxyCnt : 32 - 8;
 
     // This field must go last. When allocating this op, we will allocate extra space to hold
-    // additional ProxyCountPairs immediately after the op's allocation so we can treat this
+    // additional ViewCountPairs immediately after the op's allocation so we can treat this
     // as an fProxyCnt-length array.
-    ProxyCountPair fProxyCountPairs[1];
+    ViewCountPair fViewCountPairs[1];
 
     static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
 
@@ -843,23 +936,27 @@
 
 }  // anonymous namespace
 
-namespace GrTextureOp {
+#if GR_TEST_UTILS
+uint32_t GrTextureOp::ClassID() {
+    return TextureOp::ClassID();
+}
+#endif
 
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                               sk_sp<GrTextureProxy> proxy,
-                               GrColorType srcColorType,
-                               sk_sp<GrColorSpaceXform> textureXform,
-                               GrSamplerState::Filter filter,
-                               const SkPMColor4f& color,
-                               Saturate saturate,
-                               SkBlendMode blendMode,
-                               GrAAType aaType,
-                               GrQuadAAFlags aaFlags,
-                               const GrQuad& deviceQuad,
-                               const GrQuad& localQuad,
-                               const SkRect* domain) {
+std::unique_ptr<GrDrawOp> GrTextureOp::Make(GrRecordingContext* context,
+                                            GrSurfaceProxyView proxyView,
+                                            SkAlphaType alphaType,
+                                            sk_sp<GrColorSpaceXform> textureXform,
+                                            GrSamplerState::Filter filter,
+                                            const SkPMColor4f& color,
+                                            Saturate saturate,
+                                            SkBlendMode blendMode,
+                                            GrAAType aaType,
+                                            GrQuadAAFlags aaFlags,
+                                            const GrQuad& deviceQuad,
+                                            const GrQuad& localQuad,
+                                            const SkRect* domain) {
     // Apply optimizations that are valid whether or not using GrTextureOp or GrFillRectOp
-    if (domain && domain->contains(proxy->backingStoreBoundsRect())) {
+    if (domain && domain->contains(proxyView.proxy()->backingStoreBoundsRect())) {
         // No need for a shader-based domain if hardware clamping achieves the same effect
         domain = nullptr;
     }
@@ -869,26 +966,23 @@
     }
 
     if (blendMode == SkBlendMode::kSrcOver) {
-        return TextureOp::Make(context, std::move(proxy), std::move(textureXform), filter, color,
-                               saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
+        return TextureOp::Make(context, std::move(proxyView), std::move(textureXform), filter,
+                               color, saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
     } else {
         // Emulate complex blending using GrFillRectOp
         GrPaint paint;
         paint.setColor4f(color);
         paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
 
+        GrSurfaceProxy* proxy = proxyView.proxy();
         std::unique_ptr<GrFragmentProcessor> fp;
+        fp = GrSimpleTextureEffect::Make(sk_ref_sp(proxy), alphaType, SkMatrix::I(), filter);
         if (domain) {
-            // Update domain to match what GrTextureOp computes during tessellation, using top-left
-            // as the origin so that it doesn't depend on final texture size (which the FP handles
-            // later, as well as accounting for the true origin).
-            SkRect correctedDomain;
-            compute_domain(Domain::kYes, filter, kTopLeft_GrSurfaceOrigin, *domain,
-                           1.f, 1.f, proxy->height(), &correctedDomain);
-            fp = GrTextureDomainEffect::Make(std::move(proxy), srcColorType, SkMatrix::I(),
-                                             correctedDomain, GrTextureDomain::kClamp_Mode, filter);
-        } else {
-            fp = GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, SkMatrix::I(), filter);
+            // Update domain to match what GrTextureOp would do for bilerp, but don't do any
+            // normalization since GrTextureDomainEffect handles that and the origin.
+            SkRect correctedDomain = normalize_domain(filter, {1.f, 1.f, 0.f}, domain);
+            fp = GrDomainEffect::Make(std::move(fp), correctedDomain, GrTextureDomain::kClamp_Mode,
+                                      filter);
         }
         fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
         paint.addColorFragmentProcessor(std::move(fp));
@@ -901,20 +995,180 @@
     }
 }
 
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext* context,
-                                  const GrRenderTargetContext::TextureSetEntry set[],
-                                  int cnt,
-                                  GrSamplerState::Filter filter,
-                                  Saturate saturate,
-                                  GrAAType aaType,
-                                  SkCanvas::SrcRectConstraint constraint,
-                                  const SkMatrix& viewMatrix,
-                                  sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
-    return TextureOp::Make(context, set, cnt, filter, saturate, aaType, constraint, viewMatrix,
-                           std::move(textureColorSpaceXform));
-}
+// A helper class that assists in breaking up bulk API quad draws into manageable chunks.
+class GrTextureOp::BatchSizeLimiter {
+public:
+    BatchSizeLimiter(GrRenderTargetContext* rtc,
+                     const GrClip& clip,
+                     GrRecordingContext* context,
+                     int numEntries,
+                     GrSamplerState::Filter filter,
+                     GrTextureOp::Saturate saturate,
+                     SkCanvas::SrcRectConstraint constraint,
+                     const SkMatrix& viewMatrix,
+                     sk_sp<GrColorSpaceXform> textureColorSpaceXform)
+            : fRTC(rtc)
+            , fClip(clip)
+            , fContext(context)
+            , fFilter(filter)
+            , fSaturate(saturate)
+            , fConstraint(constraint)
+            , fViewMatrix(viewMatrix)
+            , fTextureColorSpaceXform(textureColorSpaceXform)
+            , fNumLeft(numEntries) {
+    }
 
-}  // namespace GrTextureOp
+    void createOp(const GrRenderTargetContext::TextureSetEntry set[],
+                  int clumpSize,
+                  GrAAType aaType) {
+        std::unique_ptr<GrDrawOp> op = TextureOp::Make(fContext, &set[fNumClumped], clumpSize,
+                                                       fFilter, fSaturate, aaType,
+                                                       fConstraint, fViewMatrix,
+                                                       fTextureColorSpaceXform);
+        fRTC->addDrawOp(fClip, std::move(op));
+
+        fNumLeft -= clumpSize;
+        fNumClumped += clumpSize;
+    }
+
+    int numLeft() const { return fNumLeft;  }
+    int baseIndex() const { return fNumClumped; }
+
+private:
+    GrRenderTargetContext*      fRTC;
+    const GrClip&               fClip;
+    GrRecordingContext*         fContext;
+    GrSamplerState::Filter      fFilter;
+    GrTextureOp::Saturate       fSaturate;
+    SkCanvas::SrcRectConstraint fConstraint;
+    const SkMatrix&             fViewMatrix;
+    sk_sp<GrColorSpaceXform>    fTextureColorSpaceXform;
+
+    int                         fNumLeft;
+    int                         fNumClumped = 0; // also the offset for the start of the next clump
+};
+
+// Greedily clump quad draws together until the index buffer limit is exceeded.
+void GrTextureOp::AddTextureSetOps(GrRenderTargetContext* rtc,
+                                   const GrClip& clip,
+                                   GrRecordingContext* context,
+                                   const GrRenderTargetContext::TextureSetEntry set[],
+                                   int cnt,
+                                   GrSamplerState::Filter filter,
+                                   Saturate saturate,
+                                   SkBlendMode blendMode,
+                                   GrAAType aaType,
+                                   SkCanvas::SrcRectConstraint constraint,
+                                   const SkMatrix& viewMatrix,
+                                   sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+    // First check if we can support batches as a single op
+    if (blendMode != SkBlendMode::kSrcOver ||
+        !context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
+        // Append each entry as its own op; these may still be GrTextureOps if the blend mode is
+        // src-over but the backend doesn't support dynamic state changes. Otherwise Make()
+        // automatically creates the appropriate GrFillRectOp to emulate GrTextureOp.
+        SkMatrix ctm;
+        for (int i = 0; i < cnt; ++i) {
+            float alpha = set[i].fAlpha;
+            ctm = viewMatrix;
+            if (set[i].fPreViewMatrix) {
+                ctm.preConcat(*set[i].fPreViewMatrix);
+            }
+
+            GrQuad quad, srcQuad;
+            if (set[i].fDstClipQuad) {
+                quad = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm);
+
+                SkPoint srcPts[4];
+                GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4);
+                srcQuad = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
+            } else {
+                quad = GrQuad::MakeFromRect(set[i].fDstRect, ctm);
+                srcQuad = GrQuad(set[i].fSrcRect);
+            }
+
+            const SkRect* domain = constraint == SkCanvas::kStrict_SrcRectConstraint
+                    ? &set[i].fSrcRect : nullptr;
+
+            auto op = Make(context, set[i].fProxyView, set[i].fSrcAlphaType, textureColorSpaceXform,
+                           filter, {alpha, alpha, alpha, alpha}, saturate, blendMode, aaType,
+                           set[i].fAAFlags, quad, srcQuad, domain);
+            rtc->addDrawOp(clip, std::move(op));
+        }
+        return;
+    }
+
+    // Second check if we can always just make a single op and avoid the extra iteration
+    // needed to clump things together.
+    if (cnt <= SkTMin(GrResourceProvider::MaxNumNonAAQuads(),
+                      GrResourceProvider::MaxNumAAQuads())) {
+        auto op = TextureOp::Make(context, set, cnt, filter, saturate, aaType,
+                                  constraint, viewMatrix, std::move(textureColorSpaceXform));
+        rtc->addDrawOp(clip, std::move(op));
+        return;
+    }
+
+    BatchSizeLimiter state(rtc, clip, context, cnt, filter, saturate, constraint, viewMatrix,
+                           std::move(textureColorSpaceXform));
+
+    // kNone and kMSAA never get altered
+    if (aaType == GrAAType::kNone || aaType == GrAAType::kMSAA) {
+        // Clump these into series of MaxNumNonAAQuads-sized GrTextureOps
+        while (state.numLeft() > 0) {
+            int clumpSize = SkTMin(state.numLeft(), GrResourceProvider::MaxNumNonAAQuads());
+
+            state.createOp(set, clumpSize, aaType);
+        }
+    } else {
+        // kCoverage can be downgraded to kNone. Note that the following is conservative. kCoverage
+        // can also get downgraded to kNone if all the quads are on integer coordinates and
+        // axis-aligned.
+        SkASSERT(aaType == GrAAType::kCoverage);
+
+        while (state.numLeft() > 0) {
+            GrAAType runningAA = GrAAType::kNone;
+            bool clumped = false;
+
+            for (int i = 0; i < state.numLeft(); ++i) {
+                int absIndex = state.baseIndex() + i;
+
+                if (set[absIndex].fAAFlags != GrQuadAAFlags::kNone) {
+
+                    if (i >= GrResourceProvider::MaxNumAAQuads()) {
+                        // Here we either need to boost the AA type to kCoverage, but doing so with
+                        // all the accumulated quads would overflow, or we have a set of AA quads
+                        // that has just gotten too large. In either case, calve off the existing
+                        // quads as their own TextureOp.
+                        state.createOp(
+                            set,
+                            runningAA == GrAAType::kNone ? i : GrResourceProvider::MaxNumAAQuads(),
+                            runningAA); // maybe downgrading AA here
+                        clumped = true;
+                        break;
+                    }
+
+                    runningAA = GrAAType::kCoverage;
+                } else if (runningAA == GrAAType::kNone) {
+
+                    if (i >= GrResourceProvider::MaxNumNonAAQuads()) {
+                        // Here we've found a consistent batch of non-AA quads that has gotten too
+                        // large. Calve it off as its own GrTextureOp.
+                        state.createOp(set, GrResourceProvider::MaxNumNonAAQuads(),
+                                       GrAAType::kNone); // definitely downgrading AA here
+                        clumped = true;
+                        break;
+                    }
+                }
+            }
+
+            if (!clumped) {
+                // We ran through the above loop w/o hitting a limit. Spit out this last clump of
+                // quads and call it a day.
+                state.createOp(set, state.numLeft(), runningAA); // maybe downgrading AA here
+            }
+        }
+    }
+}
 
 #if GR_TEST_UTILS
 #include "include/private/GrRecordingContext.h"
@@ -967,10 +1221,16 @@
     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
     bool useDomain = random->nextBool();
     auto saturate = random->nextBool() ? GrTextureOp::Saturate::kYes : GrTextureOp::Saturate::kNo;
-    return GrTextureOp::Make(context, std::move(proxy), GrColorType::kRGBA_8888,
-                             std::move(texXform), filter, color, saturate, SkBlendMode::kSrcOver,
-                             aaType, aaFlags, GrQuad::MakeFromRect(rect, viewMatrix),
-                             GrQuad(srcRect), useDomain ? &srcRect : nullptr);
+    GrSurfaceProxyView proxyView(
+            std::move(proxy), origin,
+            context->priv().caps()->getTextureSwizzle(format, GrColorType::kRGBA_8888));
+    auto alphaType = static_cast<SkAlphaType>(
+            random->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
+
+    return GrTextureOp::Make(context, std::move(proxyView), alphaType, std::move(texXform), filter,
+                             color, saturate, SkBlendMode::kSrcOver, aaType, aaFlags,
+                             GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect),
+                             useDomain ? &srcRect : nullptr);
 }
 
 #endif
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index da7a6b4..7fd545b 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -20,49 +20,61 @@
 struct SkRect;
 class SkMatrix;
 
-namespace GrTextureOp {
+class GrTextureOp {
+public:
 
-/**
- * Controls whether saturate() is called after the texture is color-converted to ensure all
- * color values are in 0..1 range.
- */
-enum class Saturate : bool { kNo = false, kYes = true };
+    /**
+     * Controls whether saturate() is called after the texture is color-converted to ensure all
+     * color values are in 0..1 range.
+     */
+    enum class Saturate : bool { kNo = false, kYes = true };
 
-/**
- * Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by the
- * texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using 'localQuad'
- * to map into the proxy's texture space. If non-null, 'domain' represents the boundary for the
- * strict src rect constraint. If GrAAType is kCoverage then AA is applied to the edges
- * indicated by GrQuadAAFlags. Otherwise, GrQuadAAFlags is ignored.
- *
- * This is functionally very similar to GrFillRectOp::Make, except that the GrPaint has been
- * deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
- * src over, this will return a GrFillRectOp with a paint that samples the proxy.
- */
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
-                               sk_sp<GrTextureProxy>,
-                               GrColorType srcColorType,
-                               sk_sp<GrColorSpaceXform>,
-                               GrSamplerState::Filter,
-                               const SkPMColor4f&,
-                               Saturate,
-                               SkBlendMode,
-                               GrAAType,
-                               GrQuadAAFlags,
-                               const GrQuad& deviceQuad,
-                               const GrQuad& localQuad,
-                               const SkRect* domain = nullptr);
+    /**
+     * Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by
+     * the texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using
+     * 'localQuad' to map into the proxy's texture space. If non-null, 'domain' represents the
+     * boundary for the strict src rect constraint. If GrAAType is kCoverage then AA is applied to
+     * the edges indicated by GrQuadAAFlags. Otherwise, GrQuadAAFlags is ignored.
+     *
+     * This is functionally very similar to GrFillRectOp::Make, except that the GrPaint has been
+     * deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
+     * src over, this will return a GrFillRectOp with a paint that samples the proxy.
+     */
+    static std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
+                                          GrSurfaceProxyView,
+                                          SkAlphaType srcAlphaType,
+                                          sk_sp<GrColorSpaceXform>,
+                                          GrSamplerState::Filter,
+                                          const SkPMColor4f&,
+                                          Saturate,
+                                          SkBlendMode,
+                                          GrAAType,
+                                          GrQuadAAFlags,
+                                          const GrQuad& deviceQuad,
+                                          const GrQuad& localQuad,
+                                          const SkRect* domain = nullptr);
 
-// Unlike the single-proxy factory, this only supports src-over blending.
-std::unique_ptr<GrDrawOp> MakeSet(GrRecordingContext*,
-                                  const GrRenderTargetContext::TextureSetEntry[],
-                                  int cnt,
-                                  GrSamplerState::Filter,
-                                  Saturate,
-                                  GrAAType,
-                                  SkCanvas::SrcRectConstraint,
-                                  const SkMatrix& viewMatrix,
-                                  sk_sp<GrColorSpaceXform> textureXform);
+    // Automatically falls back to using one GrFillRectOp per entry if dynamic states are not
+    // supported, or if the blend mode is not src-over.
+    static void AddTextureSetOps(GrRenderTargetContext*,
+                                 const GrClip& clip,
+                                 GrRecordingContext*,
+                                 const GrRenderTargetContext::TextureSetEntry[],
+                                 int cnt,
+                                 GrSamplerState::Filter,
+                                 Saturate,
+                                 SkBlendMode,
+                                 GrAAType,
+                                 SkCanvas::SrcRectConstraint,
+                                 const SkMatrix& viewMatrix,
+                                 sk_sp<GrColorSpaceXform> textureXform);
 
-}
+#if GR_TEST_UTILS
+    static uint32_t ClassID();
+#endif
+
+private:
+    class BatchSizeLimiter;
+};
+
 #endif  // GrTextureOp_DEFINED
diff --git a/src/gpu/text/GrStrikeCache.cpp b/src/gpu/text/GrStrikeCache.cpp
index f445c65..4a2cd29a 100644
--- a/src/gpu/text/GrStrikeCache.cpp
+++ b/src/gpu/text/GrStrikeCache.cpp
@@ -11,8 +11,10 @@
 #include "src/gpu/text/GrAtlasManager.h"
 #include "src/gpu/text/GrStrikeCache.h"
 
+#include "src/core/SkArenaAlloc.h"
 #include "src/core/SkAutoMalloc.h"
 #include "src/core/SkDistanceFieldGen.h"
+#include "src/core/SkStrikeSpec.h"
 
 GrStrikeCache::GrStrikeCache(const GrCaps* caps, size_t maxTextureBytes)
         : fPreserveStrike(nullptr)
@@ -80,12 +82,12 @@
     }
 }
 
-static bool get_packed_glyph_image(SkStrike* cache, SkGlyph* glyph, int width,
+static bool get_packed_glyph_image(const SkGlyph* glyph, int width,
                                    int height, int dstRB, GrMaskFormat expectedMaskFormat,
                                    void* dst, const SkMasks& masks) {
     SkASSERT(glyph->width() == width);
     SkASSERT(glyph->height() == height);
-    const void* src = cache->prepareImage(glyph);
+    const void* src = glyph->image();
     if (src == nullptr) {
         return false;
     }
@@ -190,11 +192,11 @@
                                    GrStrikeCache* glyphCache,
                                    GrAtlasManager* fullAtlasManager,
                                    GrGlyph* glyph,
-                                   SkStrike* skStrikeCache,
+                                   SkBulkGlyphMetricsAndImages* metricsAndImages,
                                    GrMaskFormat expectedMaskFormat,
                                    bool isScaledGlyph) {
     SkASSERT(glyph);
-    SkASSERT(skStrikeCache);
+    SkASSERT(metricsAndImages);
     SkASSERT(fCache.find(glyph->fPackedID));
 
     expectedMaskFormat = fullAtlasManager->resolveMaskFormat(expectedMaskFormat);
@@ -215,13 +217,13 @@
     }
     SkAutoSMalloc<1024> storage(size);
 
-    SkGlyph* skGlyph = skStrikeCache->glyph(glyph->fPackedID);
+    const SkGlyph* skGlyph = metricsAndImages->glyph(glyph->fPackedID);
     void* dataPtr = storage.get();
     if (addPad) {
         sk_bzero(dataPtr, size);
         dataPtr = (char*)(dataPtr) + rowBytes + bytesPerPixel;
     }
-    if (!get_packed_glyph_image(skStrikeCache, skGlyph, glyph->width(), glyph->height(),
+    if (!get_packed_glyph_image(skGlyph, glyph->width(), glyph->height(),
                                 rowBytes, expectedMaskFormat,
                                 dataPtr, glyphCache->getMasks())) {
         return GrDrawOpAtlas::ErrorCode::kError;
@@ -242,3 +244,25 @@
     }
     return result;
 }
+
+GrGlyph* GrTextStrike::getGlyph(const SkGlyph& skGlyph) {
+    GrGlyph* grGlyph = fCache.find(skGlyph.getPackedID());
+    if (grGlyph == nullptr) {
+        grGlyph = fAlloc.make<GrGlyph>(skGlyph);
+        fCache.add(grGlyph);
+    }
+    return grGlyph;
+}
+
+GrGlyph*
+GrTextStrike::getGlyph(SkPackedGlyphID packed, SkBulkGlyphMetricsAndImages* metricsAndImages) {
+    GrGlyph* grGlyph = fCache.find(packed);
+    if (grGlyph == nullptr) {
+        // We could return this to the caller, but in practice it adds code complexity for
+        // potentially little benefit(ie, if the glyph is not in our font cache, then its not
+        // in the atlas and we're going to be doing a texture upload anyways).
+        grGlyph = fAlloc.make<GrGlyph>(*metricsAndImages->glyph(packed));
+        fCache.add(grGlyph);
+    }
+    return grGlyph;
+}
diff --git a/src/gpu/text/GrStrikeCache.h b/src/gpu/text/GrStrikeCache.h
index 365e0a7..a0eaf7c 100644
--- a/src/gpu/text/GrStrikeCache.h
+++ b/src/gpu/text/GrStrikeCache.h
@@ -9,15 +9,16 @@
 #define GrStrikeCache_DEFINED
 
 #include "src/codec/SkMasks.h"
-#include "src/core/SkArenaAlloc.h"
-#include "src/core/SkStrike.h"
+#include "src/core/SkDescriptor.h"
 #include "src/core/SkTDynamicHash.h"
 #include "src/gpu/GrDrawOpAtlas.h"
 #include "src/gpu/GrGlyph.h"
 
+
 class GrAtlasManager;
 class GrGpu;
 class GrStrikeCache;
+class SkBulkGlyphMetricsAndImages;
 
 /**
  *  The GrTextStrike manages a pool of CPU backing memory for GrGlyphs. This backing memory
@@ -30,30 +31,13 @@
 public:
     GrTextStrike(const SkDescriptor& fontScalerKey);
 
-    GrGlyph* getGlyph(const SkGlyph& skGlyph) {
-        GrGlyph* grGlyph = fCache.find(skGlyph.getPackedID());
-        if (grGlyph == nullptr) {
-            grGlyph = fAlloc.make<GrGlyph>(skGlyph);
-            fCache.add(grGlyph);
-        }
-        return grGlyph;
-    }
+    GrGlyph* getGlyph(const SkGlyph& skGlyph);
 
     // This variant of the above function is called by GrAtlasTextOp. At this point, it is possible
     // that the maskformat of the glyph differs from what we expect.  In these cases we will just
     // draw a clear square.
     // skbug:4143 crbug:510931
-    GrGlyph* getGlyph(SkPackedGlyphID packed, SkStrike* skStrike) {
-        GrGlyph* grGlyph = fCache.find(packed);
-        if (grGlyph == nullptr) {
-            // We could return this to the caller, but in practice it adds code complexity for
-            // potentially little benefit(ie, if the glyph is not in our font cache, then its not
-            // in the atlas and we're going to be doing a texture upload anyways).
-            grGlyph = fAlloc.make<GrGlyph>(*skStrike->glyph(packed));
-            fCache.add(grGlyph);
-        }
-        return grGlyph;
-    }
+    GrGlyph* getGlyph(SkPackedGlyphID packed, SkBulkGlyphMetricsAndImages* metricsAndImages);
 
     // returns true if glyph successfully added to texture atlas, false otherwise.  If the glyph's
     // mask format has changed, then addGlyphToAtlas will draw a clear box.  This will almost never
@@ -62,7 +46,8 @@
     // get the actual glyph image itself when we get the glyph metrics.
     GrDrawOpAtlas::ErrorCode addGlyphToAtlas(GrResourceProvider*, GrDeferredUploadTarget*,
                                              GrStrikeCache*, GrAtlasManager*, GrGlyph*,
-                                             SkStrike*, GrMaskFormat expectedMaskFormat,
+                                             SkBulkGlyphMetricsAndImages*,
+                                             GrMaskFormat expectedMaskFormat,
                                              bool isScaledGlyph);
 
     // testing
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index 095c6fa..118b0b0 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -24,7 +24,7 @@
 }
 
 sk_sp<GrTextBlob> GrTextBlob::Make(int glyphCount,
-                                   int runCount,
+                                   bool forceWForDistanceFields,
                                    GrColor color,
                                    GrStrikeCache* strikeCache) {
     // We allocate size for the GrTextBlob itself, plus size for the vertices array,
@@ -32,10 +32,9 @@
     size_t verticesCount = glyphCount * kVerticesPerGlyph * kMaxVASize;
 
     size_t blobStart = 0;
-    size_t vertex = sk_align<alignof(char)>           (blobStart + sizeof(GrTextBlob) * 1);
-    size_t glyphs = sk_align<alignof(GrGlyph*)>       (vertex + sizeof(char) * verticesCount);
-    size_t   runs = sk_align<alignof(GrTextBlob::Run)>(glyphs + sizeof(GrGlyph*) * glyphCount);
-    size_t   size =                                   (runs + sizeof(GrTextBlob::Run) * runCount);
+    size_t vertex = sk_align<alignof(char)>     (blobStart + sizeof(GrTextBlob) * 1);
+    size_t glyphs = sk_align<alignof(GrGlyph*)> (vertex + sizeof(char) * verticesCount);
+    size_t   size =                             (glyphs + sizeof(GrGlyph*) * glyphCount);
 
     void* allocation = ::operator new (size);
 
@@ -43,36 +42,16 @@
         sk_bzero(allocation, size);
     }
 
-    sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{strikeCache}};
+    sk_sp<GrTextBlob> blob{new (allocation) GrTextBlob{strikeCache, color, forceWForDistanceFields}};
     blob->fSize = size;
 
     // setup offsets for vertices / glyphs
     blob->fVertices = SkTAddOffset<char>(blob.get(), vertex);
-    blob->fGlyphs = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
-    blob->fRuns = SkTAddOffset<GrTextBlob::Run>(blob.get(), runs);
+    blob->fGlyphs   = SkTAddOffset<GrGlyph*>(blob.get(), glyphs);
 
-    // Initialize runs
-    for (int i = 0; i < runCount; i++) {
-        new (&blob->fRuns[i]) GrTextBlob::Run{blob.get(), color};
-    }
-    blob->fRunCountLimit = runCount;
     return blob;
 }
 
-void GrTextBlob::Run::setupFont(const SkStrikeSpec& strikeSpec) {
-
-    if (fFallbackStrikeSpec != nullptr) {
-        *fFallbackStrikeSpec = strikeSpec;
-    } else {
-        fStrikeSpec = strikeSpec;
-    }
-}
-
-void GrTextBlob::Run::appendPathGlyph(const SkPath& path, SkPoint position,
-                                      SkScalar scale, bool preTransformed) {
-    fPathGlyphs.push_back(PathGlyph(path, position.x(), position.y(), scale, preTransformed));
-}
-
 bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
                                 const SkMaskFilterBase::BlurRec& blurRec,
                                 const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
@@ -163,7 +142,7 @@
 }
 
 inline std::unique_ptr<GrAtlasTextOp> GrTextBlob::makeOp(
-        const SubRun& info, int glyphCount, uint16_t run, uint16_t subRun,
+        SubRun& info, int glyphCount,
         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
         const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps& props,
         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrTextTarget* target) {
@@ -186,8 +165,7 @@
     geometry.fViewMatrix = viewMatrix;
     geometry.fClipRect = clipRect;
     geometry.fBlob = SkRef(this);
-    geometry.fRun = run;
-    geometry.fSubRun = subRun;
+    geometry.fSubRunPtr = &info;
     geometry.fColor = info.maskFormat() == kARGB_GrMaskFormat ? SK_PMColor4fWHITE : filteredColor;
     geometry.fX = x;
     geometry.fY = y;
@@ -220,103 +198,53 @@
                        const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
                        const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
 
-    // GrTextBlob::makeOp only takes uint16_t values for run and subRun indices.
-    // Encountering something larger than this is highly unlikely, so we'll just not draw it.
-    int lastRun = SkTMin(fRunCountLimit, (1 << 16)) - 1;
-    // For each run in the GrTextBlob we're going to churn through all the glyphs.
-    // Each run is broken into a path part and a Mask / DFT / ARGB part.
-    for (int runIndex = 0; runIndex <= lastRun; runIndex++) {
-
-        Run& run = fRuns[runIndex];
-
-        // first flush any path glyphs
-        if (run.fPathGlyphs.count()) {
+    for (auto& subRun : fSubRuns) {
+        if (subRun.drawAsPaths()) {
             SkPaint runPaint{paint};
-            runPaint.setAntiAlias(run.fAntiAlias);
+            runPaint.setAntiAlias(subRun.isAntiAliased());
+            // If there are shaders, blurs or styles, the path must be scaled into source
+            // space independently of the CTM. This allows the CTM to be correct for the
+            // different effects.
+            GrStyle style(runPaint);
 
-            for (int i = 0; i < run.fPathGlyphs.count(); i++) {
-                GrTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i];
+            bool scalePath = runPaint.getShader()
+                             || style.applies()
+                             || runPaint.getMaskFilter();
 
-                SkMatrix ctm;
-                const SkPath* path = &pathGlyph.fPath;
+            // The origin for the blob may have changed, so figure out the delta.
+            SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY};
+
+            for (const auto& pathGlyph : subRun.fPaths) {
+                SkMatrix ctm{viewMatrix};
+                SkMatrix pathMatrix = SkMatrix::MakeScale(subRun.fStrikeSpec.strikeToSourceRatio());
+                // Shift the original glyph location in source space to the position of the new
+                // blob.
+                pathMatrix.postTranslate(originShift.x() + pathGlyph.fOrigin.x(),
+                                         originShift.y() + pathGlyph.fOrigin.y());
 
                 // TmpPath must be in the same scope as GrShape shape below.
                 SkTLazy<SkPath> tmpPath;
-
-                // The glyph positions and glyph outlines are either in device space or in source
-                // space based on fPreTransformed.
-                if (!pathGlyph.fPreTransformed) {
-                    // Positions and outlines are in source space.
-
-                    ctm = viewMatrix;
-
-                    SkMatrix pathMatrix = SkMatrix::MakeScale(pathGlyph.fScale, pathGlyph.fScale);
-
-                    // The origin for the blob may have changed, so figure out the delta.
-                    SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY};
-
-                    // Shift the original glyph location in source space to the position of the new
-                    // blob.
-                    pathMatrix.postTranslate(originShift.x() + pathGlyph.fX,
-                                             originShift.y() + pathGlyph.fY);
-
-                    // If there are shaders, blurs or styles, the path must be scaled into source
-                    // space independently of the CTM. This allows the CTM to be correct for the
-                    // different effects.
-                    GrStyle style(runPaint);
-                    bool scalePath = runPaint.getShader()
-                                     || style.applies()
-                                     || runPaint.getMaskFilter();
-                    if (!scalePath) {
-                        // Scale can be applied to CTM -- no effects.
-
-                        ctm.preConcat(pathMatrix);
-                    } else {
-                        // Scale the outline into source space.
-
-                        // Transform the path form the normalized outline to source space. This
-                        // way the CTM will remain the same so it can be used by the effects.
-                        SkPath* sourceOutline = tmpPath.init();
-                        path->transform(pathMatrix, sourceOutline);
-                        sourceOutline->setIsVolatile(true);
-                        path = sourceOutline;
-                    }
-
-
+                const SkPath* path = &pathGlyph.fPath;
+                if (!scalePath) {
+                    // Scale can be applied to CTM -- no effects.
+                    ctm.preConcat(pathMatrix);
                 } else {
-                    // Positions and outlines are in device space.
+                    // Scale the outline into source space.
 
-                    SkPoint originalOrigin = {fInitialX, fInitialY};
-                    fInitialViewMatrix.mapPoints(&originalOrigin, 1);
-
-                    SkPoint newOrigin = {x, y};
-                    viewMatrix.mapPoints(&newOrigin, 1);
-
-                    // The origin shift in device space.
-                    SkPoint originShift = newOrigin - originalOrigin;
-
-                    // Shift the original glyph location in device space to the position of the
-                    // new blob.
-                    ctm = SkMatrix::MakeTrans(originShift.x() + pathGlyph.fX,
-                                              originShift.y() + pathGlyph.fY);
+                    // Transform the path form the normalized outline to source space. This
+                    // way the CTM will remain the same so it can be used by the effects.
+                    SkPath* sourceOutline = tmpPath.init();
+                    path->transform(pathMatrix, sourceOutline);
+                    sourceOutline->setIsVolatile(true);
+                    path = sourceOutline;
                 }
 
                 // TODO: we are losing the mutability of the path here
                 GrShape shape(*path, paint);
-
                 target->drawShape(clip, runPaint, ctm, shape);
             }
-        }
-
-        // then flush each subrun, if any
-        if (!run.fInitialized) {
-            continue;
-        }
-
-        int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1;
-        for (int subRun = 0; subRun <= lastSubRun; subRun++) {
-            const SubRun& info = run.fSubRunInfo[subRun];
-            int glyphCount = info.glyphCount();
+        } else {
+            int glyphCount = subRun.glyphCount();
             if (0 == glyphCount) {
                 continue;
             }
@@ -329,14 +257,13 @@
             GrAA aa;
             // We can clip geometrically if we're not using SDFs or transformed glyphs,
             // and we have an axis-aligned rectangular non-AA clip
-            if (!info.drawAsDistanceFields() && !info.needsTransform() &&
+            if (!subRun.drawAsDistanceFields() && !subRun.needsTransform() &&
                 clip.isRRect(rtBounds, &clipRRect, &aa) &&
                 clipRRect.isRect() && GrAA::kNo == aa) {
                 skipClip = true;
                 // We only need to do clipping work if the subrun isn't contained by the clip
                 SkRect subRunBounds;
-                this->computeSubRunBounds(&subRunBounds, runIndex, subRun, viewMatrix, x, y,
-                                          false);
+                this->computeSubRunBounds(&subRunBounds, subRun, viewMatrix, x, y, false);
                 if (!clipRRect.getBounds().contains(subRunBounds)) {
                     // If the subrun is completely outside, don't add an op for it
                     if (!clipRRect.getBounds().intersects(subRunBounds)) {
@@ -349,7 +276,7 @@
             }
 
             if (submitOp) {
-                auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y,
+                auto op = this->makeOp(subRun, glyphCount, viewMatrix, x, y,
                                        clipRect, paint, filteredColor, props, distanceAdjustTable,
                                        target);
                 if (op) {
@@ -362,18 +289,17 @@
                 }
             }
         }
-
     }
 }
 
 std::unique_ptr<GrDrawOp> GrTextBlob::test_makeOp(
-        int glyphCount, uint16_t run, uint16_t subRun, const SkMatrix& viewMatrix,
+        int glyphCount, const SkMatrix& viewMatrix,
         SkScalar x, SkScalar y, const SkPaint& paint, const SkPMColor4f& filteredColor,
         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
         GrTextTarget* target) {
-    const GrTextBlob::SubRun& info = fRuns[run].fSubRunInfo[subRun];
+    GrTextBlob::SubRun& info = fSubRuns[0];
     SkIRect emptyRect = SkIRect::MakeEmpty();
-    return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, emptyRect,
+    return this->makeOp(info, glyphCount, viewMatrix, x, y, emptyRect,
                         paint, filteredColor, props, distanceAdjustTable, target);
 }
 
@@ -393,21 +319,11 @@
     SkASSERT_RELEASE(l.fMinMaxScale == r.fMinMaxScale);
     SkASSERT_RELEASE(l.fTextType == r.fTextType);
 
-    SkASSERT_RELEASE(l.fRunCountLimit == r.fRunCountLimit);
-    for (int i = 0; i < l.fRunCountLimit; i++) {
-        const Run& lRun = l.fRuns[i];
-        const Run& rRun = r.fRuns[i];
-
-        SkASSERT_RELEASE(lRun.fStrikeSpec.descriptor() == rRun.fStrikeSpec.descriptor());
-
-        // color can be changed
-        //SkASSERT(lRun.fColor == rRun.fColor);
-        SkASSERT_RELEASE(lRun.fInitialized == rRun.fInitialized);
-
-        SkASSERT_RELEASE(lRun.fSubRunInfo.count() == rRun.fSubRunInfo.count());
-        for(int j = 0; j < lRun.fSubRunInfo.count(); j++) {
-            const SubRun& lSubRun = lRun.fSubRunInfo[j];
-            const SubRun& rSubRun = rRun.fSubRunInfo[j];
+    for(auto t : SkMakeZip(l.fSubRuns, r.fSubRuns)) {
+        const SubRun& lSubRun = std::get<0>(t);
+        const SubRun& rSubRun = std::get<1>(t);
+        SkASSERT(lSubRun.drawAsPaths() == rSubRun.drawAsPaths());
+        if (!lSubRun.drawAsPaths()) {
 
             // TODO we can do this check, but we have to apply the VM to the old vertex bounds
             //SkASSERT_RELEASE(lSubRun.vertexBounds() == rSubRun.vertexBounds());
@@ -422,21 +338,18 @@
             }
 
             SkASSERT_RELEASE(lSubRun.vertexStartIndex() == rSubRun.vertexStartIndex());
-            SkASSERT_RELEASE(lSubRun.vertexEndIndex() == rSubRun.vertexEndIndex());
             SkASSERT_RELEASE(lSubRun.glyphStartIndex() == rSubRun.glyphStartIndex());
-            SkASSERT_RELEASE(lSubRun.glyphEndIndex() == rSubRun.glyphEndIndex());
             SkASSERT_RELEASE(lSubRun.maskFormat() == rSubRun.maskFormat());
             SkASSERT_RELEASE(lSubRun.drawAsDistanceFields() == rSubRun.drawAsDistanceFields());
             SkASSERT_RELEASE(lSubRun.hasUseLCDText() == rSubRun.hasUseLCDText());
-        }
-
-        SkASSERT_RELEASE(lRun.fPathGlyphs.count() == rRun.fPathGlyphs.count());
-        for (int i = 0; i < lRun.fPathGlyphs.count(); i++) {
-            const Run::PathGlyph& lPathGlyph = lRun.fPathGlyphs[i];
-            const Run::PathGlyph& rPathGlyph = rRun.fPathGlyphs[i];
-
-            SkASSERT_RELEASE(lPathGlyph.fPath == rPathGlyph.fPath);
-            // We can't assert that these have the same translations
+        } else {
+            SkASSERT_RELEASE(lSubRun.fPaths.size() == rSubRun.fPaths.size());
+            for(auto p : SkMakeZip(lSubRun.fPaths, rSubRun.fPaths)) {
+                const PathGlyph& lPath = std::get<0>(p);
+                const PathGlyph& rPath = std::get<1>(p);
+                SkASSERT_RELEASE(lPath.fPath == rPath.fPath);
+                // We can't assert that these have the same translations
+            }
         }
     }
 }
@@ -445,7 +358,7 @@
                                                 SkScalar x, SkScalar y, SkScalar* transX,
                                                 SkScalar* transY) {
     // Don't use the matrix to translate on distance field for fallback subruns.
-    calculate_translation(!this->drawAsDistanceFields() && !this->isFallback(), viewMatrix,
+    calculate_translation(!this->drawAsDistanceFields() && !this->needsTransform(), viewMatrix,
             x, y, fCurrentViewMatrix, fX, fY, transX, transY);
     fCurrentViewMatrix = viewMatrix;
     fX = x;
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index b354510..c6b6739 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -25,6 +25,7 @@
 #include "src/gpu/text/GrTextTarget.h"
 
 class GrAtlasManager;
+class GrAtlasTextOp;
 struct GrDistanceFieldAdjustTable;
 struct GrGlyph;
 
@@ -33,6 +34,8 @@
 
 // With this flag enabled, the GrTextContext will, as a sanity check, regenerate every blob
 // that comes in to verify the integrity of its cache
+// This is of dubious value, and maybe should be removed. I checked it on 11/21/2019, and many
+// tests failed.
 #define CACHE_SANITY_CHECK 0
 
 /*
@@ -45,38 +48,32 @@
  * The only thing(aside from a memcopy) required to flush a GrTextBlob is to ensure that
  * the GrAtlas will not evict anything the Blob needs.
  *
- * Note: This struct should really be named GrCachedAtasTextBlob, but that is too verbose.
- *
  * *WARNING* If you add new fields to this struct, then you may need to to update AssertEqual
  */
 class GrTextBlob : public SkNVRefCnt<GrTextBlob>, public SkGlyphRunPainterInterface {
-    struct Run;
 public:
+    class SubRun;
     SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrTextBlob);
+    using SubRunBufferSpec = std::tuple<uint32_t, uint32_t, size_t, size_t>;
 
     class VertexRegenerator;
 
     void generateFromGlyphRunList(const GrShaderCaps& shaderCaps,
                                   const GrTextContext::Options& options,
                                   const SkPaint& paint,
-                                  SkScalerContextFlags scalerContextFlags,
                                   const SkMatrix& viewMatrix,
                                   const SkSurfaceProps& props,
                                   const SkGlyphRunList& glyphRunList,
                                   SkGlyphRunListPainter* glyphPainter);
 
+    // Make an empty GrTextBlob, with all the invariants set to make the right decisions when
+    // adding SubRuns.
     static sk_sp<GrTextBlob> Make(
             int glyphCount,
-            int runCount,
+            bool forceWForDistanceFields,
             GrColor color,
             GrStrikeCache* strikeCache);
 
-    /**
-     * We currently force regeneration of a blob if old or new matrix differ in having perspective.
-     * If we ever change that then the key must contain the perspectiveness when there are distance
-     * fields as perspective distance field use 3 component vertex positions and non-perspective
-     * uses 2.
-     */
     struct Key {
         Key() {
             sk_bzero(this, sizeof(Key));
@@ -134,28 +131,16 @@
     void setHasDistanceField() { fTextType |= kHasDistanceField_TextType; }
     void setHasBitmap() { fTextType |= kHasBitmap_TextType; }
 
-    int runCountLimit() const { return fRunCountLimit; }
-
-    Run* pushBackRun() {
-        SkASSERT(fRunCount < fRunCountLimit);
-
-        // If there is more run, then connect up the subruns.
-        if (fRunCount > 0) {
-            SubRun& newRun = fRuns[fRunCount].fSubRunInfo.back();
-            SubRun& lastRun = fRuns[fRunCount - 1].fSubRunInfo.back();
-            newRun.setAsSuccessor(lastRun);
-        }
-
-        fRunCount++;
-        return this->currentRun();
-    }
-
     void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax) {
         // we init fMaxMinScale and fMinMaxScale in the constructor
         fMaxMinScale = SkMaxScalar(scaledMin, fMaxMinScale);
         fMinMaxScale = SkMinScalar(scaledMax, fMinMaxScale);
     }
 
+    bool hasPerspective() const {
+        return fInitialViewMatrix.hasPerspective();
+    }
+
     static size_t GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
         switch (maskFormat) {
             case kA8_GrMaskFormat:
@@ -176,7 +161,7 @@
                const SkPaint& paint, const SkPMColor4f& filteredColor, const GrClip& clip,
                const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
 
-    void computeSubRunBounds(SkRect* outBounds, int runIndex, int subRunIndex,
+    void computeSubRunBounds(SkRect* outBounds, const SubRun& subRun,
                              const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                              bool needsGlyphTransform) {
         // We don't yet position distance field text on the cpu, so we have to map the vertex bounds
@@ -184,8 +169,6 @@
         // We handle vertex bounds differently for distance field text and bitmap text because
         // the vertex bounds of bitmap text are in device space.  If we are flushing multiple runs
         // from one blob then we are going to pay the price here of mapping the rect for each run.
-        const Run& run = fRuns[runIndex];
-        const SubRun& subRun = run.fSubRunInfo[subRunIndex];
         *outBounds = subRun.vertexBounds();
         if (needsGlyphTransform) {
             // Distance field text is positioned with the (X,Y) as part of the glyph position,
@@ -238,22 +221,21 @@
 
     size_t size() const { return fSize; }
 
-    ~GrTextBlob() override {
-        for (int i = 0; i < fRunCountLimit; i++) {
-            fRuns[i].~Run();
-        }
-    }
+    ~GrTextBlob() override { }
 
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Internal test methods
-    std::unique_ptr<GrDrawOp> test_makeOp(int glyphCount, uint16_t run, uint16_t subRun,
+    std::unique_ptr<GrDrawOp> test_makeOp(int glyphCount,
                                           const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                                           const SkPaint& paint, const SkPMColor4f& filteredColor,
                                           const SkSurfaceProps&, const GrDistanceFieldAdjustTable*,
                                           GrTextTarget*);
 
 private:
-    GrTextBlob(GrStrikeCache* strikeCache) : fStrikeCache{strikeCache} { }
+    GrTextBlob(GrStrikeCache* strikeCache, GrColor color, bool forceWForDistanceFields)
+        : fColor{color}
+        , fStrikeCache{strikeCache}
+        , fForceWForDistanceFields{forceWForDistanceFields} { }
 
     // This function will only be called when we are generating a blob from scratch. We record the
     // initial view matrix and initial offsets(x,y), because we record vertex bounds relative to
@@ -266,25 +248,54 @@
         }
         fInitialX = x;
         fInitialY = y;
-
-        // make sure all initial subruns have the correct VM and X/Y applied
-        for (int i = 0; i < fRunCountLimit; i++) {
-            fRuns[i].fSubRunInfo[0].init(fInitialViewMatrix, x, y);
-        }
     }
 
+public:
+    // Any glyphs that can't be rendered with the base or override descriptor
+    // are rendered as paths
+    struct PathGlyph {
+        PathGlyph(const SkPath& path, SkPoint origin)
+                : fPath(path)
+                , fOrigin(origin) {}
+        SkPath fPath;
+        SkPoint fOrigin;
+    };
+
+    enum SubRunType {
+        kDirectMask,
+        kTransformedMask,
+        kTransformedPath,
+        kTransformedSDFT
+    };
+
+    bool hasW(SubRunType type) const {
+        if (type == kTransformedSDFT) {
+            return this->hasPerspective() || fForceWForDistanceFields;
+        } else if (type == kTransformedMask || type == kTransformedPath) {
+            return this->hasPerspective();
+        }
+
+        // The viewMatrix is implicitly SkMatrix::I when drawing kDirectMask, because it is not
+        // used.
+        return false;
+    }
+
+    // Hold data to draw the different types of sub run. SubRuns are produced knowing all the
+    // glyphs that are included in them.
     class SubRun {
     public:
-        SubRun(Run* run, const SkStrikeSpec& strikeSpec, GrColor color)
-            : fColor{color}
-            , fRun{run}
-            , fStrikeSpec{strikeSpec} {}
+        // SubRun for masks
+        SubRun(SubRunType type,
+               GrTextBlob* textBlob,
+               const SkStrikeSpec& strikeSpec,
+               GrMaskFormat format,
+               const SubRunBufferSpec& bufferSpec,
+               sk_sp<GrTextStrike>&& grStrike);
 
-        // When used with emplace_back, this constructs a SubRun from the last SubRun in an array.
-        //SubRun(SkSTArray<1, SubRun>* subRunList)
-        //    : fColor{subRunList->fromBack(1).fColor} { }
+        // SubRun for paths
+        SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
 
-        void appendGlyph(GrGlyph* glyph, SkRect dstRect);
+        void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
 
         // TODO when this object is more internal, drop the privacy
         void resetBulkUseToken() { fBulkUseToken.reset(); }
@@ -296,29 +307,15 @@
         void setAtlasGeneration(uint64_t atlasGeneration) { fAtlasGeneration = atlasGeneration;}
         uint64_t atlasGeneration() const { return fAtlasGeneration; }
 
-        size_t byteCount() const { return fVertexEndIndex - fVertexStartIndex; }
         size_t vertexStartIndex() const { return fVertexStartIndex; }
-        size_t vertexEndIndex() const { return fVertexEndIndex; }
-
         uint32_t glyphCount() const { return fGlyphEndIndex - fGlyphStartIndex; }
         uint32_t glyphStartIndex() const { return fGlyphStartIndex; }
-        uint32_t glyphEndIndex() const { return fGlyphEndIndex; }
+
         void setColor(GrColor color) { fColor = color; }
         GrColor color() const { return fColor; }
-        void setMaskFormat(GrMaskFormat format) { fMaskFormat = format; }
+
         GrMaskFormat maskFormat() const { return fMaskFormat; }
 
-        void setAsSuccessor(const SubRun& prev) {
-            fGlyphStartIndex = prev.glyphEndIndex();
-            fGlyphEndIndex = fGlyphStartIndex;
-
-            fVertexStartIndex = prev.vertexEndIndex();
-            fVertexEndIndex = fVertexStartIndex;
-
-            // copy over viewmatrix settings
-            this->init(prev.fCurrentViewMatrix, prev.fX, prev.fY);
-        }
-
         const SkRect& vertexBounds() const { return fVertexBounds; }
         void joinGlyphBounds(const SkRect& glyphBounds) {
             fVertexBounds.joinNonEmptyArg(glyphBounds);
@@ -334,229 +331,120 @@
         void computeTranslation(const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                                 SkScalar* transX, SkScalar* transY);
 
+        bool drawAsDistanceFields() const { return fType == kTransformedSDFT; }
+        bool drawAsPaths() const { return fType == kTransformedPath; }
+        bool needsTransform() const {
+            return fType == kTransformedPath
+            || fType == kTransformedMask
+            || fType == kTransformedSDFT;
+        }
+        bool hasW() const {
+            return fBlob->hasW(fType);
+        }
+
         // df properties
-        void setDrawAsDistanceFields() { fFlags.drawAsSdf = true; }
-        bool drawAsDistanceFields() const { return fFlags.drawAsSdf; }
         void setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
         bool hasUseLCDText() const { return fFlags.useLCDText; }
         void setAntiAliased(bool antiAliased) { fFlags.antiAliased = antiAliased; }
         bool isAntiAliased() const { return fFlags.antiAliased; }
-        void setHasWCoord(bool hasW) { fFlags.hasWCoord = hasW; }
-        bool hasWCoord() const { return fFlags.hasWCoord; }
-        void setNeedsTransform(bool needsTransform) { fFlags.needsTransform = needsTransform; }
-        bool needsTransform() const { return fFlags.needsTransform; }
-        void setFallback() { fFlags.argbFallback = true; }
-        bool isFallback() { return fFlags.argbFallback; }
 
         const SkStrikeSpec& strikeSpec() const { return fStrikeSpec; }
 
-    private:
-        GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
+        const SubRunType fType;
+        GrTextBlob* const fBlob;
+        const GrMaskFormat fMaskFormat;
+        const uint32_t fGlyphStartIndex;
+        const uint32_t fGlyphEndIndex;
+        const size_t fVertexStartIndex;
+        const size_t fVertexEndIndex;
+        const SkStrikeSpec fStrikeSpec;
         sk_sp<GrTextStrike> fStrike;
-        SkMatrix fCurrentViewMatrix;
-        SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
-        uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
-        size_t fVertexStartIndex{0};
-        size_t fVertexEndIndex{0};
-        uint32_t fGlyphStartIndex{0};
-        uint32_t fGlyphEndIndex{0};
-        SkScalar fX;
-        SkScalar fY;
-        GrColor fColor{GrColor_ILLEGAL};
-        GrMaskFormat fMaskFormat{kA8_GrMaskFormat};
         struct {
-            bool drawAsSdf:1;
             bool useLCDText:1;
             bool antiAliased:1;
-            bool hasWCoord:1;
-            bool needsTransform:1;
-            bool argbFallback:1;
-        } fFlags{false, false, false, false, false, false};
-        Run* const fRun;
-        const SkStrikeSpec& fStrikeSpec;
-    };  // SubRunInfo
-
-    /*
-     * Each Run inside of the blob can have its texture coordinates regenerated if required.
-     * To determine if regeneration is necessary, fAtlasGeneration is used.  If there have been
-     * any evictions inside of the atlas, then we will simply regenerate Runs.  We could track
-     * this at a more fine grained level, but its not clear if this is worth it, as evictions
-     * should be fairly rare.
-     *
-     * One additional point, each run can contain glyphs with any of the three mask formats.
-     * We call these SubRuns.  Because a subrun must be a contiguous range, we have to create
-     * a new subrun each time the mask format changes in a run.  In theory, a run can have as
-     * many SubRuns as it has glyphs, ie if a run alternates between color emoji and A8.  In
-     * practice, the vast majority of runs have only a single subrun.
-     *
-     * Finally, for runs where the entire thing is too large for the GrTextContext to
-     * handle, we have a bit to mark the run as flushable via rendering as paths or as scaled
-     * glyphs. It would be a bit expensive to figure out ahead of time whether or not a run
-     * can flush in this manner, so we always allocate vertices for the run, regardless of
-     * whether or not it is too large.  The benefit of this strategy is that we can always reuse
-     * a blob allocation regardless of viewmatrix changes.  We could store positions for these
-     * glyphs, however, it's not clear if this is a win because we'd still have to either go to the
-     * glyph cache to get the path at flush time, or hold onto the path in the cache, which
-     * would greatly increase the memory of these cached items.
-     */
-    struct Run {
-        explicit Run(GrTextBlob* blob, GrColor color)
-        : fBlob{blob}, fColor{color} {
-            // To ensure we always have one subrun, we push back a fresh run here
-            fSubRunInfo.emplace_back(this, fStrikeSpec, color);
-        }
-
-        // sets the last subrun of runIndex to use w values
-        void setSubRunHasW(bool hasWCoord) {
-            SubRun& subRun = this->fSubRunInfo.back();
-            subRun.setHasWCoord(hasWCoord);
-        }
-
-        // inits the override descriptor on the current run.  All following subruns must use this
-        // descriptor
-        SubRun* initARGBFallback() {
-            fFallbackStrikeSpec.reset(new SkStrikeSpec{});
-            // Push back a new subrun to fill and set the override descriptor
-            SubRun* subRun = this->pushBackSubRun(*fFallbackStrikeSpec, fColor);
-            subRun->setMaskFormat(kARGB_GrMaskFormat);
-            subRun->setFallback();
-            return subRun;
-        }
-
-        // Appends a glyph to the blob as a path only.
-        void appendPathGlyph(
-                const SkPath& path, SkPoint position, SkScalar scale, bool preTransformed);
-
-        // Append a glyph to the sub run taking care to switch the glyph if needed.
-        void switchSubRunIfNeededAndAppendGlyph(GrGlyph* glyph,
-                                                const sk_sp<GrTextStrike>& strike,
-                                                const SkRect& destRect,
-                                                bool needsTransform);
-
-        // Used when the glyph in the cache has the CTM already applied, therefore no transform
-        // is needed during rendering.
-        void appendDeviceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
-                                    const SkGlyph& skGlyph,
-                                    SkPoint origin);
-
-        // The glyph is oriented upright in the cache and needs to be transformed onto the screen.
-        void appendSourceSpaceGlyph(const sk_sp<GrTextStrike>& strike,
-                                    const SkGlyph& skGlyph,
-                                    SkPoint origin,
-                                    SkScalar textScale);
-
-        void setupFont(const SkStrikeSpec& strikeSpec);
-
-        void setRunFontAntiAlias(bool aa) {
-            fAntiAlias = aa;
-        }
-
-        // sets the last subrun of runIndex to use distance field text
-        void setSubRunHasDistanceFields(bool hasLCD, bool isAntiAlias, bool hasWCoord) {
-            SubRun& subRun = fSubRunInfo.back();
-            subRun.setUseLCDText(hasLCD);
-            subRun.setAntiAliased(isAntiAlias);
-            subRun.setDrawAsDistanceFields();
-            subRun.setHasWCoord(hasWCoord);
-        }
-
-        SubRun* pushBackSubRun(const SkStrikeSpec& desc, GrColor color) {
-            // Forward glyph / vertex information to seed the new sub run
-            SubRun& newSubRun = fSubRunInfo.emplace_back(this, desc, color);
-
-            const SubRun& prevSubRun = fSubRunInfo.fromBack(1);
-
-            // Forward glyph / vertex information to seed the new sub run
-            newSubRun.setAsSuccessor(prevSubRun);
-            return &newSubRun;
-        }
-
-        // Any glyphs that can't be rendered with the base or override descriptor
-        // are rendered as paths
-        struct PathGlyph {
-            PathGlyph(const SkPath& path, SkScalar x, SkScalar y, SkScalar scale, bool preXformed)
-                : fPath(path)
-                , fX(x)
-                , fY(y)
-                , fScale(scale)
-                , fPreTransformed(preXformed) {}
-            SkPath fPath;
-            SkScalar fX;
-            SkScalar fY;
-            SkScalar fScale;
-            bool fPreTransformed;
-        };
-
-        SkSTArray<1, SubRun> fSubRunInfo;
-        SkStrikeSpec fStrikeSpec;
-
-        // Distance field text cannot draw coloremoji, and so has to fall back.  However,
-        // though the distance field text and the coloremoji may share the same run, they
-        // will have different descriptors.  If fFallbackStrikeSpec is non-nullptr, then it
-        // will be used in place of the run's descriptor to regen texture coords
-        std::unique_ptr<SkStrikeSpec> fFallbackStrikeSpec;
-
-        SkTArray<PathGlyph> fPathGlyphs;
-
-        bool fAntiAlias{false};   // needed mainly for rendering paths
-        bool fInitialized{false};
-
-        GrTextBlob* const fBlob;
+        } fFlags{false, false};
         GrColor fColor;
-    };  // Run
+        GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
+        SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
+        uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
+        SkScalar fX;
+        SkScalar fY;
+        SkMatrix fCurrentViewMatrix;
+        std::vector<PathGlyph> fPaths;
+    };  // SubRun
 
+    SubRun* makeSubRun(SubRunType type,
+                       const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                       const SkStrikeSpec& strikeSpec,
+                       GrMaskFormat format);
+
+    void addSingleMaskFormat(
+            SubRunType type,
+            const SkZip<SkGlyphVariant, SkPoint>& drawables,
+            const SkStrikeSpec& strikeSpec,
+            GrMaskFormat format);
+
+    void addMultiMaskFormat(
+            SubRunType type,
+            const SkZip<SkGlyphVariant, SkPoint>& drawables,
+            const SkStrikeSpec& strikeSpec);
+
+    void addSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                 const SkStrikeSpec& strikeSpec,
+                 const SkFont& runFont,
+                 SkScalar minScale,
+                 SkScalar maxScale);
+
+private:
     std::unique_ptr<GrAtlasTextOp> makeOp(
-            const SubRun& info, int glyphCount, uint16_t run, uint16_t subRun,
+            SubRun& info, int glyphCount,
             const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
             const SkPaint& paint, const SkPMColor4f& filteredColor, const SkSurfaceProps&,
             const GrDistanceFieldAdjustTable*, GrTextTarget*);
 
-    // currentRun, startRun, and the process* calls are all used by the SkGlyphRunPainter, and
-    // live in SkGlyphRunPainter.cpp file.
-    Run* currentRun();
-
-    void startRun(const SkGlyphRun& glyphRun, bool useSDFT) override;
-
     void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                             const SkStrikeSpec& strikeSpec) override;
 
     void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                            const SkFont& runFont,
                             const SkStrikeSpec& strikeSpec) override;
 
     void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawables,
                            const SkStrikeSpec& strikeSpec,
                            const SkFont& runFont,
                            SkScalar minScale,
-                           SkScalar maxScale,
-                           bool hasWCoord) override;
+                           SkScalar maxScale) override;
 
-    void processSourceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                               const SkStrikeSpec& strikeSpec,
-                               bool hasW) override;
+    void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
+                            const SkStrikeSpec& strikeSpec) override;
 
-    void processDeviceFallback(const SkZip<SkGlyphVariant, SkPoint>& drawables,
-                               const SkStrikeSpec& strikeSpec) override;
+
+
+    // Pool of bytes for vertex data.
+    char* fVertices;
+    // How much (in bytes) of the vertex data is used while accumulating SubRuns.
+    size_t fVerticesCursor{0};
+    // Pointers to every glyph that will be drawn.
+    GrGlyph** fGlyphs;
+    // Number of glyphs stored in fGlyphs while accumulating SubRuns.
+    uint32_t fGlyphsCursor{0};
+
+    // Assume one run per text blob.
+    SkSTArray<1, SubRun> fSubRuns;
+
+    // The color of the text to draw for solid colors.
+    const GrColor fColor;
+
+    // Lifetime: The GrStrikeCache is owned by and has the same lifetime as the GrRecordingContext.
+    // The GrRecordingContext also owns the GrTextBlob cache which owns this GrTextBlob.
+    GrStrikeCache* const fStrikeCache;
+    SkMaskFilterBase::BlurRec fBlurRec;
 
     struct StrokeInfo {
         SkScalar fFrameWidth;
         SkScalar fMiterLimit;
         SkPaint::Join fJoin;
     };
-
-    enum TextType {
-        kHasDistanceField_TextType = 0x1,
-        kHasBitmap_TextType = 0x2,
-    };
-
-    // all glyph / vertex offsets are into these pools.
-    char* fVertices;
-    GrGlyph** fGlyphs;
-    Run* fRuns;
-
-    // Lifetime: The GrStrikeCache is owned by and has the same lifetime as the GrRecordingContext.
-    // The GrRecordingContext also owns the GrTextBlob cache which owns this GrTextBlob.
-    GrStrikeCache* const fStrikeCache;
-    SkMaskFilterBase::BlurRec fBlurRec;
     StrokeInfo fStrokeInfo;
     Key fKey;
     SkMatrix fInitialViewMatrix;
@@ -571,8 +459,14 @@
     // maximum minimum scale, and minimum maximum scale, we can support before we need to regen
     SkScalar fMaxMinScale{-SK_ScalarMax};
     SkScalar fMinMaxScale{SK_ScalarMax};
-    int fRunCount{0};
-    int fRunCountLimit;
+
+    // From the distance field options to force distance fields to have a W coordinate.
+    const bool fForceWForDistanceFields;
+
+    enum TextType {
+        kHasDistanceField_TextType = 0x1,
+        kHasBitmap_TextType = 0x2,
+    };
     uint8_t fTextType{0};
 };
 
@@ -591,10 +485,10 @@
      * SkAutoGlyphCache is reused then it can save the cost of multiple detach/attach operations of
      * SkGlyphCache.
      */
-    VertexRegenerator(GrResourceProvider*, GrTextBlob*, int runIdx, int subRunIdx,
+    VertexRegenerator(GrResourceProvider*, GrTextBlob*,
+            GrTextBlob::SubRun* subRun,
                       const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color,
-                      GrDeferredUploadTarget*, GrStrikeCache*, GrAtlasManager*,
-                      SkExclusiveStrikePtr*);
+                      GrDeferredUploadTarget*, GrStrikeCache*, GrAtlasManager*);
 
     struct Result {
         /**
@@ -624,9 +518,9 @@
     const SkMatrix& fViewMatrix;
     GrTextBlob* fBlob;
     GrDeferredUploadTarget* fUploadTarget;
-    GrStrikeCache* fGlyphCache;
+    GrStrikeCache* fGrStrikeCache;
     GrAtlasManager* fFullAtlasManager;
-    SkExclusiveStrikePtr* fLazyStrike;
+    SkTLazy<SkBulkGlyphMetricsAndImages> fMetricsAndImages;
     SubRun* fSubRun;
     GrColor fColor;
     SkScalar fTransX;
diff --git a/src/gpu/text/GrTextBlobCache.h b/src/gpu/text/GrTextBlobCache.h
index 5479112..49f2430 100644
--- a/src/gpu/text/GrTextBlobCache.h
+++ b/src/gpu/text/GrTextBlobCache.h
@@ -34,19 +34,20 @@
     ~GrTextBlobCache();
 
     sk_sp<GrTextBlob> makeBlob(const SkGlyphRunList& glyphRunList,
+                               bool forceW,
                                GrColor color,
                                GrStrikeCache* strikeCache) {
-        return GrTextBlob::Make(
-                glyphRunList.totalGlyphCount(), glyphRunList.size(), color, strikeCache);
+        return GrTextBlob::Make(glyphRunList.totalGlyphCount(), forceW, color, strikeCache);
     }
 
     sk_sp<GrTextBlob> makeCachedBlob(const SkGlyphRunList& glyphRunList,
                                      const GrTextBlob::Key& key,
                                      const SkMaskFilterBase::BlurRec& blurRec,
                                      const SkPaint& paint,
+                                     bool forceW,
                                      GrColor color,
                                      GrStrikeCache* strikeCache) {
-        sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList, color, strikeCache));
+        sk_sp<GrTextBlob> cacheBlob(makeBlob(glyphRunList, forceW, color, strikeCache));
         cacheBlob->setupKey(key, blurRec, paint);
         this->add(cacheBlob);
         glyphRunList.temporaryShuntBlobNotifyAddedToCache(fUniqueID);
diff --git a/src/gpu/text/GrTextBlobVertexRegenerator.cpp b/src/gpu/text/GrTextBlobVertexRegenerator.cpp
index 976beaf..f465dc3 100644
--- a/src/gpu/text/GrTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrTextBlobVertexRegenerator.cpp
@@ -117,21 +117,19 @@
 
 GrTextBlob::VertexRegenerator::VertexRegenerator(GrResourceProvider* resourceProvider,
                                                  GrTextBlob* blob,
-                                                 int runIdx, int subRunIdx,
+                                                 GrTextBlob::SubRun* subRun,
                                                  const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                                                  GrColor color,
                                                  GrDeferredUploadTarget* uploadTarget,
-                                                 GrStrikeCache* glyphCache,
-                                                 GrAtlasManager* fullAtlasManager,
-                                                 SkExclusiveStrikePtr* lazyStrike)
+                                                 GrStrikeCache* grStrikeCache,
+                                                 GrAtlasManager* fullAtlasManager)
         : fResourceProvider(resourceProvider)
         , fViewMatrix(viewMatrix)
         , fBlob(blob)
         , fUploadTarget(uploadTarget)
-        , fGlyphCache(glyphCache)
+        , fGrStrikeCache(grStrikeCache)
         , fFullAtlasManager(fullAtlasManager)
-        , fLazyStrike(lazyStrike)
-        , fSubRun(&blob->fRuns[runIdx].fSubRunInfo[subRunIdx])
+        , fSubRun(subRun)
         , fColor(color) {
     // Compute translation if any
     fSubRun->computeTranslation(fViewMatrix, x, y, &fTransX, &fTransY);
@@ -160,25 +158,39 @@
                                             bool regenPos, bool regenCol, bool regenTexCoords,
                                             bool regenGlyphs) {
     SkASSERT(!regenGlyphs || regenTexCoords);
-    sk_sp<GrTextStrike> strike;
     if (regenTexCoords) {
         fSubRun->resetBulkUseToken();
 
         const SkStrikeSpec& strikeSpec = fSubRun->strikeSpec();
 
-        if (!*fLazyStrike || (*fLazyStrike)->getDescriptor() != strikeSpec.descriptor()) {
-            *fLazyStrike =
-                    strikeSpec.findOrCreateExclusiveStrike(SkStrikeCache::GlobalStrikeCache());
+        if (!fMetricsAndImages.isValid()
+            || fMetricsAndImages->descriptor() != strikeSpec.descriptor()) {
+            fMetricsAndImages.init(strikeSpec);
         }
 
         if (regenGlyphs) {
-            strike = strikeSpec.findOrCreateGrStrike(fGlyphCache);
-        } else {
-            strike = fSubRun->refStrike();
+            // Take the glyphs from the old strike, and translate them a new strike.
+            sk_sp<GrTextStrike> newStrike = strikeSpec.findOrCreateGrStrike(fGrStrikeCache);
+
+            // Start this batch at the start of the subRun plus any glyphs that were previously
+            // processed.
+            size_t glyphStart = fSubRun->glyphStartIndex() + fCurrGlyph;
+            SkSpan<GrGlyph*> glyphs{&(fBlob->fGlyphs[glyphStart]),
+                                    fSubRun->glyphCount() - fCurrGlyph};
+
+            // Convert old glyphs to newStrike.
+            for (auto& glyph : glyphs) {
+                SkPackedGlyphID id = glyph->fPackedID;
+                glyph = newStrike->getGlyph(id, fMetricsAndImages.get());
+                SkASSERT(id == glyph->fPackedID);
+            }
+
+            fSubRun->setStrike(newStrike);
         }
     }
 
-    bool hasW = fSubRun->hasWCoord();
+    sk_sp<GrTextStrike> grStrike = fSubRun->refStrike();
+    bool hasW = fSubRun->hasW();
     auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
     char* currVertex = fBlob->fVertices + fSubRun->vertexStartIndex() +
                        fCurrGlyph * kVerticesPerGlyph * vertexStride;
@@ -188,23 +200,15 @@
         GrGlyph* glyph = nullptr;
         if (regenTexCoords) {
             size_t glyphOffset = glyphIdx + fSubRun->glyphStartIndex();
-
-            if (regenGlyphs) {
-                // Get the id from the old glyph, and use the new strike to lookup
-                // the glyph.
-                SkPackedGlyphID id = fBlob->fGlyphs[glyphOffset]->fPackedID;
-                fBlob->fGlyphs[glyphOffset] = strike->getGlyph(id, fLazyStrike->get());
-                SkASSERT(id == fBlob->fGlyphs[glyphOffset]->fPackedID);
-            }
             glyph = fBlob->fGlyphs[glyphOffset];
             SkASSERT(glyph && glyph->fMaskFormat == fSubRun->maskFormat());
 
             if (!fFullAtlasManager->hasGlyph(glyph)) {
                 GrDrawOpAtlas::ErrorCode code;
-                code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
-                                              fFullAtlasManager, glyph,
-                                              fLazyStrike->get(), fSubRun->maskFormat(),
-                                              fSubRun->needsTransform());
+                code = grStrike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGrStrikeCache,
+                                                 fFullAtlasManager, glyph,
+                                                 fMetricsAndImages.get(), fSubRun->maskFormat(),
+                                                 fSubRun->needsTransform());
                 if (GrDrawOpAtlas::ErrorCode::kError == code) {
                     // Something horrible has happened - drop the op
                     return false;
@@ -238,9 +242,6 @@
     // We may have changed the color so update it here
     fSubRun->setColor(fColor);
     if (regenTexCoords) {
-        if (regenGlyphs) {
-            fSubRun->setStrike(std::move(strike));
-        }
         fSubRun->setAtlasGeneration(fBrokenRun
                                     ? GrDrawOpAtlas::kInvalidAtlasGeneration
                                     : fFullAtlasManager->atlasGeneration(fSubRun->maskFormat()));
@@ -268,7 +269,7 @@
                              fRegenFlags & kRegenTex,
                              fRegenFlags & kRegenGlyph);
     } else {
-        bool hasW = fSubRun->hasWCoord();
+        bool hasW = fSubRun->hasW();
         auto vertexStride = GetVertexStride(fSubRun->maskFormat(), hasW);
         result->fFinished = true;
         result->fGlyphsRegenerated = fSubRun->glyphCount() - fCurrGlyph;
diff --git a/src/gpu/text/GrTextContext.cpp b/src/gpu/text/GrTextContext.cpp
index f1ecef4..76fa383 100644
--- a/src/gpu/text/GrTextContext.cpp
+++ b/src/gpu/text/GrTextContext.cpp
@@ -32,10 +32,16 @@
 static const int kMediumDFFontSize = 72;
 static const int kMediumDFFontLimit = 72;
 static const int kLargeDFFontSize = 162;
+#ifdef SK_BUILD_FOR_MAC
+static const int kLargeDFFontLimit = 162;
+static const int kExtraLargeDFFontSize = 256;
+#endif
 
 static const int kDefaultMinDistanceFieldFontSize = 18;
-#ifdef SK_BUILD_FOR_ANDROID
+#if defined(SK_BUILD_FOR_ANDROID)
 static const int kDefaultMaxDistanceFieldFontSize = 384;
+#elif defined(SK_BUILD_FOR_MAC)
+static const int kDefaultMaxDistanceFieldFontSize = kExtraLargeDFFontSize;
 #else
 static const int kDefaultMaxDistanceFieldFontSize = 2 * kLargeDFFontSize;
 #endif
@@ -167,10 +173,20 @@
     } else if (scaledTextSize <= kMediumDFFontLimit) {
         *textRatio = textSize / kMediumDFFontSize;
         dfFont.setSize(SkIntToScalar(kMediumDFFontSize));
+#ifdef SK_BUILD_FOR_MAC
+    } else if (scaledTextSize <= kLargeDFFontLimit) {
+        *textRatio = textSize / kLargeDFFontSize;
+        dfFont.setSize(SkIntToScalar(kLargeDFFontSize));
+    } else {
+        *textRatio = textSize / kExtraLargeDFFontSize;
+        dfFont.setSize(SkIntToScalar(kExtraLargeDFFontSize));
+    }
+#else
     } else {
         *textRatio = textSize / kLargeDFFontSize;
         dfFont.setSize(SkIntToScalar(kLargeDFFontSize));
     }
+#endif
 
     dfFont.setEdging(SkFont::Edging::kAntiAlias);
     dfFont.setForceAutoHinting(false);
diff --git a/src/gpu/vk/GrVkBuffer.cpp b/src/gpu/vk/GrVkBuffer.cpp
index 833dfdb5..53780ade 100644
--- a/src/gpu/vk/GrVkBuffer.cpp
+++ b/src/gpu/vk/GrVkBuffer.cpp
@@ -19,7 +19,7 @@
 #define VALIDATE() do {} while(false)
 #endif
 
-const GrVkBuffer::Resource* GrVkBuffer::Create(const GrVkGpu* gpu, const Desc& desc) {
+const GrVkBuffer::Resource* GrVkBuffer::Create(GrVkGpu* gpu, const Desc& desc) {
     SkASSERT(!gpu->protectedContext() || (gpu->protectedContext() == desc.fDynamic));
     VkBuffer       buffer;
     GrVkAlloc      alloc;
diff --git a/src/gpu/vk/GrVkBuffer.h b/src/gpu/vk/GrVkBuffer.h
index e82613d..625df99 100644
--- a/src/gpu/vk/GrVkBuffer.h
+++ b/src/gpu/vk/GrVkBuffer.h
@@ -77,7 +77,7 @@
     };
 
     // convenience routine for raw buffer creation
-    static const Resource* Create(const GrVkGpu* gpu,
+    static const Resource* Create(GrVkGpu* gpu,
                                   const Desc& descriptor);
 
     GrVkBuffer(const Desc& desc, const GrVkBuffer::Resource* resource)
diff --git a/src/gpu/vk/GrVkBufferView.cpp b/src/gpu/vk/GrVkBufferView.cpp
deleted file mode 100644
index f4ed9ea..0000000
--- a/src/gpu/vk/GrVkBufferView.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/gpu/vk/GrVkBufferView.h"
-#include "src/gpu/vk/GrVkGpu.h"
-#include "src/gpu/vk/GrVkUtil.h"
-
-const GrVkBufferView* GrVkBufferView::Create(const GrVkGpu* gpu, VkBuffer buffer, VkFormat format,
-                                             VkDeviceSize offset, VkDeviceSize range) {
-    VkBufferView bufferView;
-
-    // Create the VkBufferView
-    VkBufferViewCreateInfo viewInfo;
-    memset(&viewInfo, 0, sizeof(VkBufferViewCreateInfo));
-    viewInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
-    viewInfo.pNext = nullptr;
-    viewInfo.flags = 0;
-    viewInfo.buffer = buffer;
-    viewInfo.format = format;
-    viewInfo.offset = offset;
-    viewInfo.range = range;
-
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateBufferView(gpu->device(), &viewInfo,
-                                                                   nullptr, &bufferView));
-    if (err) {
-        return nullptr;
-    }
-
-    return new GrVkBufferView(bufferView);
-}
-
-void GrVkBufferView::freeGPUData(GrVkGpu* gpu) const {
-    GR_VK_CALL(gpu->vkInterface(), DestroyBufferView(gpu->device(), fBufferView, nullptr));
-}
diff --git a/src/gpu/vk/GrVkBufferView.h b/src/gpu/vk/GrVkBufferView.h
deleted file mode 100644
index e6adf6b..0000000
--- a/src/gpu/vk/GrVkBufferView.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrVkBufferView_DEFINED
-#define GrVkBufferView_DEFINED
-
-#include "include/gpu/GrTypes.h"
-#include "include/gpu/vk/GrVkTypes.h"
-#include "src/gpu/vk/GrVkResource.h"
-
-class GrVkBufferView : public GrVkResource {
-public:
-    static const GrVkBufferView* Create(const GrVkGpu* gpu, VkBuffer buffer, VkFormat format,
-                                        VkDeviceSize offset, VkDeviceSize range);
-
-    VkBufferView bufferView() const { return fBufferView; }
-
-#ifdef SK_TRACE_VK_RESOURCES
-    void dumpInfo() const override {
-        SkDebugf("GrVkBufferView: %d (%d refs)\n", fBufferView, this->getRefCnt());
-    }
-#endif
-
-private:
-    GrVkBufferView(VkBufferView bufferView) : INHERITED(), fBufferView(bufferView) {}
-
-    void freeGPUData(GrVkGpu* gpu) const override;
-
-    VkBufferView  fBufferView;
-
-    typedef GrVkResource INHERITED;
-};
-
-#endif
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 36e2436..90fbd4f 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -8,13 +8,17 @@
 #include "include/gpu/GrBackendSurface.h"
 #include "include/gpu/vk/GrVkBackendContext.h"
 #include "include/gpu/vk/GrVkExtensions.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrRenderTarget.h"
 #include "src/gpu/GrRenderTargetProxy.h"
 #include "src/gpu/GrShaderCaps.h"
+#include "src/gpu/GrStencilSettings.h"
 #include "src/gpu/GrUtil.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/vk/GrVkCaps.h"
+#include "src/gpu/vk/GrVkGpu.h"
 #include "src/gpu/vk/GrVkInterface.h"
+#include "src/gpu/vk/GrVkRenderTarget.h"
 #include "src/gpu/vk/GrVkTexture.h"
 #include "src/gpu/vk/GrVkUniformHandler.h"
 #include "src/gpu/vk/GrVkUtil.h"
@@ -388,8 +392,7 @@
         this->applyDriverCorrectnessWorkarounds(properties);
     }
 
-    this->applyOptionsOverrides(contextOptions);
-    fShaderCaps->applyOptionsOverrides(contextOptions);
+    this->finishInitialization(contextOptions);
 }
 
 void GrVkCaps::applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties& properties) {
@@ -442,6 +445,20 @@
     // GrCaps workarounds
     ////////////////////////////////////////////////////////////////////////////
 
+    // The GTX660 bot experiences crashes and incorrect rendering with MSAA CCPR. Block this path
+    // renderer on non-mixed-sampled NVIDIA.
+    // NOTE: We may lose mixed samples support later if the context options suppress dual source
+    // blending, but that shouldn't be an issue because MSAA CCPR seems to work fine (even without
+    // mixed samples) on later NVIDIA hardware where mixed samples would be supported.
+    if ((kNvidia_VkVendor == properties.vendorID) && !fMixedSamplesSupport) {
+        fDriverBlacklistMSAACCPR = true;
+    }
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // MSAA CCPR is slow on Android. http://skbug.com/9676
+    fDriverBlacklistMSAACCPR = true;
+#endif
+
     if (kARM_VkVendor == properties.vendorID) {
         fInstanceAttribSupport = false;
         fAvoidWritePixelsFastPath = true; // bugs.skia.org/8064
@@ -459,29 +476,12 @@
     if (kImagination_VkVendor == properties.vendorID) {
         fShaderCaps->fAtan2ImplementedAsAtanYOverX = true;
     }
-}
 
-int get_max_sample_count(VkSampleCountFlags flags) {
-    SkASSERT(flags & VK_SAMPLE_COUNT_1_BIT);
-    if (!(flags & VK_SAMPLE_COUNT_2_BIT)) {
-        return 0;
+    if (kQualcomm_VkVendor == properties.vendorID) {
+        // The sample mask round rect op draws nothing on Adreno for the srcmode gm.
+        // http://skbug.com/8921
+        fShaderCaps->fCanOnlyUseSampleMaskWithStencil = true;
     }
-    if (!(flags & VK_SAMPLE_COUNT_4_BIT)) {
-        return 2;
-    }
-    if (!(flags & VK_SAMPLE_COUNT_8_BIT)) {
-        return 4;
-    }
-    if (!(flags & VK_SAMPLE_COUNT_16_BIT)) {
-        return 8;
-    }
-    if (!(flags & VK_SAMPLE_COUNT_32_BIT)) {
-        return 16;
-    }
-    if (!(flags & VK_SAMPLE_COUNT_64_BIT)) {
-        return 32;
-    }
-    return 64;
 }
 
 void GrVkCaps::initGrCaps(const GrVkInterface* vkInterface,
@@ -497,6 +497,19 @@
     static const uint32_t kMaxVertexAttributes = 64;
     fMaxVertexAttributes = SkTMin(properties.limits.maxVertexInputAttributes, kMaxVertexAttributes);
 
+    if (properties.limits.standardSampleLocations) {
+        fSampleLocationsSupport = true;
+    }
+
+    if (extensions.hasExtension(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME, 1)) {
+        // We "disable" multisample by colocating all samples at pixel center.
+        fMultisampleDisableSupport = true;
+    }
+
+    if (extensions.hasExtension(VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME, 1)) {
+        fMixedSamplesSupport = true;
+    }
+
     // We could actually query and get a max size for each config, however maxImageDimension2D will
     // give the minimum max size across all configs. So for simplicity we will use that for now.
     fMaxRenderTargetSize = SkTMin(properties.limits.maxImageDimension2D, (uint32_t)INT_MAX);
@@ -563,7 +576,7 @@
     // to be true with Vulkan as well.
     shaderCaps->fPreferFlatInterpolation = kQualcomm_VkVendor != properties.vendorID;
 
-    // GrShaderCaps
+    shaderCaps->fSampleMaskSupport = true;
 
     shaderCaps->fShaderDerivativeSupport = true;
 
@@ -1187,12 +1200,8 @@
     if (flags & VK_SAMPLE_COUNT_16_BIT) {
         fColorSampleCounts.push_back(16);
     }
-    if (flags & VK_SAMPLE_COUNT_32_BIT) {
-        fColorSampleCounts.push_back(32);
-    }
-    if (flags & VK_SAMPLE_COUNT_64_BIT) {
-        fColorSampleCounts.push_back(64);
-    }
+    // Standard sample locations are not defined for more than 16 samples, and we don't need more
+    // than 16. Omit 32 and 64.
 }
 
 void GrVkCaps::FormatInfo::init(const GrVkInterface* interface,
@@ -1597,6 +1606,11 @@
         case GrColorType::kAlpha_8xxx:
         case GrColorType::kAlpha_F32xxx:
         case GrColorType::kGray_8xxx:
+        case GrColorType::kRGB_888:
+        case GrColorType::kR_8:
+        case GrColorType::kR_16:
+        case GrColorType::kR_F16:
+        case GrColorType::kGray_F16:
             break;
     }
 
@@ -1717,6 +1731,76 @@
     return GrVkUniformHandler::kUniformBufferDescSet;
 }
 
+void GrVkCaps::addExtraSamplerKey(GrProcessorKeyBuilder* b,
+                                  const GrSamplerState& samplerState,
+                                  const GrBackendFormat& format) const {
+    const GrVkYcbcrConversionInfo* ycbcrInfo = format.getVkYcbcrConversionInfo();
+    if (!ycbcrInfo) {
+        return;
+    }
+
+    GrVkSampler::Key key = GrVkSampler::GenerateKey(samplerState, *ycbcrInfo);
+
+    size_t numInts = (sizeof(key) + 3) / 4;
+
+    uint32_t* tmp = b->add32n(numInts);
+
+    tmp[numInts - 1] = 0;
+    memcpy(tmp, &key, sizeof(key));
+}
+
+/**
+ * For Vulkan we want to cache the entire VkPipeline for reuse of draws. The Desc here holds all
+ * the information needed to differentiate one pipeline from another.
+ *
+ * The GrProgramDesc contains all the information need to create the actual shaders for the
+ * pipeline.
+ *
+ * For Vulkan we need to add to the GrProgramDesc to include the rest of the state on the
+ * pipline. This includes stencil settings, blending information, render pass format, draw face
+ * information, and primitive type. Note that some state is set dynamically on the pipeline for
+ * each draw  and thus is not included in this descriptor. This includes the viewport, scissor,
+ * and blend constant.
+ */
+GrProgramDesc GrVkCaps::makeDesc(const GrRenderTarget* rt, const GrProgramInfo& programInfo) const {
+    GrProgramDesc desc;
+    if (!GrProgramDesc::Build(&desc, rt, programInfo, *this)) {
+        SkASSERT(!desc.isValid());
+        return desc;
+    }
+
+    GrProcessorKeyBuilder b(&desc.key());
+
+    // This will become part of the sheared off key used to persistently cache
+    // the SPIRV code. It needs to be added right after the base key so that,
+    // when the base-key is sheared off, the shearing code can include it in the
+    // reduced key (c.f. the +4s in the SkData::MakeWithCopy calls in
+    // GrVkPipelineStateBuilder.cpp).
+    b.add32(GrVkGpu::kShader_PersistentCacheKeyType);
+
+    GrVkRenderTarget* vkRT = (GrVkRenderTarget*) rt;
+    // TODO: support failure in getSimpleRenderPass
+    SkASSERT(vkRT->getSimpleRenderPass());
+    vkRT->getSimpleRenderPass()->genKey(&b);
+
+    GrStencilSettings stencil = programInfo.nonGLStencilSettings();
+    stencil.genKey(&b);
+
+    programInfo.pipeline().genKey(&b, *this);
+    b.add32(programInfo.numRasterSamples());
+
+    // Vulkan requires the full primitive type as part of its key
+    b.add32((uint32_t)programInfo.primitiveType());
+
+    if (this->mixedSamplesSupport()) {
+        // Add "0" to indicate that coverage modulation will not be enabled, or the (non-zero)
+        // raster sample count if it will.
+        b.add32(!programInfo.isMixedSampled() ? 0 : programInfo.numRasterSamples());
+    }
+
+    return desc;
+}
+
 #if GR_TEST_UTILS
 std::vector<GrCaps::TestFormatColorTypeCombination> GrVkCaps::getTestingCombinations() const {
     std::vector<GrCaps::TestFormatColorTypeCombination> combos = {
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 0b90035..38fb367 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -184,6 +184,12 @@
     int getFragmentUniformBinding() const;
     int getFragmentUniformSet() const;
 
+    void addExtraSamplerKey(GrProcessorKeyBuilder*,
+                            const GrSamplerState&,
+                            const GrBackendFormat&) const override;
+
+    GrProgramDesc makeDesc(const GrRenderTarget*, const GrProgramInfo&) const override;
+
 #if GR_TEST_UTILS
     std::vector<TestFormatColorTypeCombination> getTestingCombinations() const override;
 #endif
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 5f95ccf..2722e51 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -40,23 +40,15 @@
     }
 }
 
-void GrVkCommandBuffer::freeGPUData(GrVkGpu* gpu) const {
+void GrVkCommandBuffer::freeGPUData(GrVkGpu* gpu, VkCommandPool cmdPool) const {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     SkASSERT(!fIsActive);
-    for (int i = 0; i < fTrackedResources.count(); ++i) {
-        fTrackedResources[i]->notifyRemovedFromCommandBuffer();
-        fTrackedResources[i]->unref(gpu);
-    }
+    SkASSERT(!fTrackedResources.count());
+    SkASSERT(!fTrackedRecycledResources.count());
+    SkASSERT(cmdPool != VK_NULL_HANDLE);
+    SkASSERT(!this->isWrapped());
 
-    for (int i = 0; i < fTrackedRecycledResources.count(); ++i) {
-        fTrackedRecycledResources[i]->notifyRemovedFromCommandBuffer();
-        fTrackedRecycledResources[i]->recycle(const_cast<GrVkGpu*>(gpu));
-    }
-
-    if (!this->isWrapped()) {
-        GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), fCmdPool->vkCommandPool(),
-                                                          1, &fCmdBuffer));
-    }
+    GR_VK_CALL(gpu->vkInterface(), FreeCommandBuffers(gpu->device(), cmdPool, 1, &fCmdBuffer));
 
     this->onFreeGPUData(gpu);
 }
@@ -376,27 +368,26 @@
     SkASSERT(!fActiveRenderPass);
 }
 
-GrVkPrimaryCommandBuffer* GrVkPrimaryCommandBuffer::Create(const GrVkGpu* gpu,
-                                                           GrVkCommandPool* cmdPool) {
+GrVkPrimaryCommandBuffer* GrVkPrimaryCommandBuffer::Create(GrVkGpu* gpu,
+                                                           VkCommandPool cmdPool) {
     const VkCommandBufferAllocateInfo cmdInfo = {
         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
         nullptr,                                          // pNext
-        cmdPool->vkCommandPool(),                         // commandPool
+        cmdPool,                                          // commandPool
         VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
         1                                                 // bufferCount
     };
 
     VkCommandBuffer cmdBuffer;
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), AllocateCommandBuffers(gpu->device(),
-                                                                         &cmdInfo,
-                                                                         &cmdBuffer));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, AllocateCommandBuffers(gpu->device(), &cmdInfo, &cmdBuffer));
     if (err) {
         return nullptr;
     }
-    return new GrVkPrimaryCommandBuffer(cmdBuffer, cmdPool);
+    return new GrVkPrimaryCommandBuffer(cmdBuffer);
 }
 
-void GrVkPrimaryCommandBuffer::begin(const GrVkGpu* gpu) {
+void GrVkPrimaryCommandBuffer::begin(GrVkGpu* gpu) {
     SkASSERT(!fIsActive);
     VkCommandBufferBeginInfo cmdBufferBeginInfo;
     memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
@@ -405,8 +396,7 @@
     cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
     cmdBufferBeginInfo.pInheritanceInfo = nullptr;
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer,
-                                                               &cmdBufferBeginInfo));
+    GR_VK_CALL_ERRCHECK(gpu, BeginCommandBuffer(fCmdBuffer, &cmdBufferBeginInfo));
     fIsActive = true;
 }
 
@@ -416,21 +406,26 @@
 
     this->submitPipelineBarriers(gpu);
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
+    GR_VK_CALL_ERRCHECK(gpu, EndCommandBuffer(fCmdBuffer));
     this->invalidateState();
     fIsActive = false;
     fHasWork = false;
 }
 
-void GrVkPrimaryCommandBuffer::beginRenderPass(const GrVkGpu* gpu,
+bool GrVkPrimaryCommandBuffer::beginRenderPass(GrVkGpu* gpu,
                                                const GrVkRenderPass* renderPass,
                                                const VkClearValue clearValues[],
-                                               const GrVkRenderTarget& target,
+                                               GrVkRenderTarget* target,
                                                const SkIRect& bounds,
                                                bool forSecondaryCB) {
     SkASSERT(fIsActive);
     SkASSERT(!fActiveRenderPass);
-    SkASSERT(renderPass->isCompatible(target));
+    SkASSERT(renderPass->isCompatible(*target));
+
+    const GrVkFramebuffer* framebuffer = target->getFramebuffer();
+    if (!framebuffer) {
+        return false;
+    }
 
     this->addingWork(gpu);
 
@@ -443,7 +438,7 @@
     beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
     beginInfo.pNext = nullptr;
     beginInfo.renderPass = renderPass->vkRenderPass();
-    beginInfo.framebuffer = target.framebuffer()->framebuffer();
+    beginInfo.framebuffer = framebuffer->framebuffer();
     beginInfo.renderArea = renderArea;
     beginInfo.clearValueCount = renderPass->clearValueCount();
     beginInfo.pClearValues = clearValues;
@@ -454,7 +449,8 @@
     GR_VK_CALL(gpu->vkInterface(), CmdBeginRenderPass(fCmdBuffer, &beginInfo, contents));
     fActiveRenderPass = renderPass;
     this->addResource(renderPass);
-    target.addResources(*this);
+    target->addResources(*this);
+    return true;
 }
 
 void GrVkPrimaryCommandBuffer::endRenderPass(const GrVkGpu* gpu) {
@@ -470,7 +466,6 @@
     // The Vulkan spec allows secondary command buffers to be executed on a primary command buffer
     // if the command pools both were created from were created with the same queue family. However,
     // we currently always create them from the same pool.
-    SkASSERT(buffer->commandPool() == fCmdPool);
     SkASSERT(fIsActive);
     SkASSERT(!buffer->fIsActive);
     SkASSERT(fActiveRenderPass);
@@ -485,7 +480,7 @@
     this->invalidateState();
 }
 
-static void submit_to_queue(const GrVkInterface* interface,
+static void submit_to_queue(GrVkGpu* gpu,
                             VkQueue queue,
                             VkFence fence,
                             uint32_t waitCount,
@@ -515,11 +510,11 @@
     submitInfo.pCommandBuffers = commandBuffers;
     submitInfo.signalSemaphoreCount = signalCount;
     submitInfo.pSignalSemaphores = signalSemaphores;
-    GR_VK_CALL_ERRCHECK(interface, QueueSubmit(queue, 1, &submitInfo, fence));
+    GR_VK_CALL_ERRCHECK(gpu, QueueSubmit(queue, 1, &submitInfo, fence));
 }
 
 void GrVkPrimaryCommandBuffer::submitToQueue(
-        const GrVkGpu* gpu,
+        GrVkGpu* gpu,
         VkQueue queue,
         GrVkGpu::SyncQueue sync,
         SkTArray<GrVkSemaphore::Resource*>& signalSemaphores,
@@ -544,8 +539,7 @@
     if (0 == signalCount && 0 == waitCount) {
         // This command buffer has no dependent semaphores so we can simply just submit it to the
         // queue with no worries.
-        submit_to_queue(gpu->vkInterface(), queue, fSubmitFence, 0, nullptr, nullptr, 1,
-                        &fCmdBuffer, 0, nullptr,
+        submit_to_queue(gpu, queue, fSubmitFence, 0, nullptr, nullptr, 1, &fCmdBuffer, 0, nullptr,
                         gpu->protectedContext() ? GrProtected::kYes : GrProtected::kNo);
     } else {
         SkTArray<VkSemaphore> vkSignalSems(signalCount);
@@ -565,8 +559,8 @@
                 vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
             }
         }
-        submit_to_queue(gpu->vkInterface(), queue, fSubmitFence, vkWaitSems.count(),
-                        vkWaitSems.begin(), vkWaitStages.begin(), 1, &fCmdBuffer,
+        submit_to_queue(gpu, queue, fSubmitFence, vkWaitSems.count(), vkWaitSems.begin(),
+                        vkWaitStages.begin(), 1, &fCmdBuffer,
                         vkSignalSems.count(), vkSignalSems.begin(),
                         gpu->protectedContext() ? GrProtected::kYes : GrProtected::kNo);
         for (int i = 0; i < signalCount; ++i) {
@@ -580,11 +574,10 @@
     if (GrVkGpu::kForce_SyncQueue == sync) {
         err = GR_VK_CALL(gpu->vkInterface(),
                          WaitForFences(gpu->device(), 1, &fSubmitFence, true, UINT64_MAX));
-        if (VK_TIMEOUT == err) {
-            SkDebugf("Fence failed to signal: %d\n", err);
+        if (VK_SUCCESS != err) {
+            SkDebugf("Fence failed: %d\n", err);
             SK_ABORT("failing");
         }
-        SkASSERT(!err);
 
         fFinishedProcs.reset();
 
@@ -628,10 +621,9 @@
     fFinishedProcs.reset();
 }
 
-void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers(GrVkGpu* gpu) {
+void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers(GrVkCommandPool* cmdPool) {
     for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
-        SkASSERT(fSecondaryCommandBuffers[i]->commandPool() == fCmdPool);
-        fSecondaryCommandBuffers[i].release()->recycle(gpu);
+        fSecondaryCommandBuffers[i].release()->recycle(cmdPool);
     }
     fSecondaryCommandBuffers.reset();
 }
@@ -848,9 +840,7 @@
     if (VK_NULL_HANDLE != fSubmitFence) {
         GR_VK_CALL(gpu->vkInterface(), DestroyFence(gpu->device(), fSubmitFence, nullptr));
     }
-    for (const auto& buffer : fSecondaryCommandBuffers) {
-        buffer->freeGPUData(gpu);
-    }
+    SkASSERT(!fSecondaryCommandBuffers.count());
 }
 
 void GrVkPrimaryCommandBuffer::onAbandonGPUData() const {
@@ -864,7 +854,7 @@
 // SecondaryCommandBuffer
 ////////////////////////////////////////////////////////////////////////////////
 
-GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(const GrVkGpu* gpu,
+GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(GrVkGpu* gpu,
                                                                GrVkCommandPool* cmdPool) {
     SkASSERT(cmdPool);
     const VkCommandBufferAllocateInfo cmdInfo = {
@@ -876,20 +866,19 @@
     };
 
     VkCommandBuffer cmdBuffer;
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), AllocateCommandBuffers(gpu->device(),
-                                                                         &cmdInfo,
-                                                                         &cmdBuffer));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, AllocateCommandBuffers(gpu->device(), &cmdInfo, &cmdBuffer));
     if (err) {
         return nullptr;
     }
-    return new GrVkSecondaryCommandBuffer(cmdBuffer, cmdPool);
+    return new GrVkSecondaryCommandBuffer(cmdBuffer, false);
 }
 
 GrVkSecondaryCommandBuffer* GrVkSecondaryCommandBuffer::Create(VkCommandBuffer cmdBuffer) {
-    return new GrVkSecondaryCommandBuffer(cmdBuffer, nullptr);
+    return new GrVkSecondaryCommandBuffer(cmdBuffer, true);
 }
 
-void GrVkSecondaryCommandBuffer::begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
+void GrVkSecondaryCommandBuffer::begin(GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
                                        const GrVkRenderPass* compatibleRenderPass) {
     SkASSERT(!fIsActive);
     SkASSERT(compatibleRenderPass);
@@ -915,8 +904,7 @@
                 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
         cmdBufferBeginInfo.pInheritanceInfo = &inheritanceInfo;
 
-        GR_VK_CALL_ERRCHECK(gpu->vkInterface(), BeginCommandBuffer(fCmdBuffer,
-                                                                   &cmdBufferBeginInfo));
+        GR_VK_CALL_ERRCHECK(gpu, BeginCommandBuffer(fCmdBuffer, &cmdBufferBeginInfo));
     }
     fIsActive = true;
 }
@@ -924,19 +912,18 @@
 void GrVkSecondaryCommandBuffer::end(GrVkGpu* gpu) {
     SkASSERT(fIsActive);
     if (!this->isWrapped()) {
-        GR_VK_CALL_ERRCHECK(gpu->vkInterface(), EndCommandBuffer(fCmdBuffer));
+        GR_VK_CALL_ERRCHECK(gpu, EndCommandBuffer(fCmdBuffer));
     }
     this->invalidateState();
     fIsActive = false;
     fHasWork = false;
 }
 
-void GrVkSecondaryCommandBuffer::recycle(GrVkGpu* gpu) {
+void GrVkSecondaryCommandBuffer::recycle(GrVkCommandPool* cmdPool) {
     if (this->isWrapped()) {
-        this->freeGPUData(gpu);
         delete this;
     } else {
-        fCmdPool->recycleSecondaryCommandBuffer(this);
+        cmdPool->recycleSecondaryCommandBuffer(this);
     }
 }
 
diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h
index ec35c12..1328cf0 100644
--- a/src/gpu/vk/GrVkCommandBuffer.h
+++ b/src/gpu/vk/GrVkCommandBuffer.h
@@ -62,8 +62,6 @@
                             uint32_t dynamicOffsetCount,
                             const uint32_t* dynamicOffsets);
 
-    GrVkCommandPool* commandPool() { return fCmdPool; }
-
     void setViewport(const GrVkGpu* gpu,
                      uint32_t firstViewport,
                      uint32_t viewportCount,
@@ -99,6 +97,7 @@
     // Add ref-counted resource that will be tracked and released when this command buffer finishes
     // execution
     void addResource(const GrVkResource* resource) {
+        SkASSERT(resource);
         resource->ref();
         resource->notifyAddedToCommandBuffer();
         fTrackedResources.append(1, &resource);
@@ -114,25 +113,21 @@
 
     void releaseResources(GrVkGpu* gpu);
 
-    void freeGPUData(GrVkGpu* gpu) const;
+    void freeGPUData(GrVkGpu* gpu, VkCommandPool pool) const;
     void abandonGPUData() const;
 
     bool hasWork() const { return fHasWork; }
 
 protected:
-    GrVkCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool,
-                      const GrVkRenderPass* rp = nullptr)
-            : fIsActive(false)
-            , fActiveRenderPass(rp)
-            , fCmdBuffer(cmdBuffer)
-            , fCmdPool(cmdPool)
-            , fNumResets(0) {
+    GrVkCommandBuffer(VkCommandBuffer cmdBuffer, bool isWrapped = false)
+            : fCmdBuffer(cmdBuffer)
+            , fIsWrapped(isWrapped) {
         fTrackedResources.setReserve(kInitialTrackedResourcesCount);
         fTrackedRecycledResources.setReserve(kInitialTrackedResourcesCount);
         this->invalidateState();
     }
 
-    bool isWrapped() const { return fCmdPool == nullptr; }
+    bool isWrapped() const { return fIsWrapped; }
 
     void addingWork(const GrVkGpu* gpu);
 
@@ -143,20 +138,16 @@
 
     // Tracks whether we are in the middle of a command buffer begin/end calls and thus can add
     // new commands to the buffer;
-    bool                      fIsActive;
+    bool                      fIsActive = false;
     bool                      fHasWork = false;
 
     // Stores a pointer to the current active render pass (i.e. begin has been called but not
     // end). A nullptr means there is no active render pass. The GrVKCommandBuffer does not own
     // the render pass.
-    const GrVkRenderPass*     fActiveRenderPass;
+    const GrVkRenderPass*     fActiveRenderPass = nullptr;
 
     VkCommandBuffer           fCmdBuffer;
 
-    // Raw pointer, not refcounted. The command pool controls the command buffer's lifespan, so
-    // it's guaranteed to outlive us.
-    GrVkCommandPool*          fCmdPool;
-
 private:
     static const int kInitialTrackedResourcesCount = 32;
 
@@ -174,7 +165,7 @@
     // all arrays growing to the max size, after so many resets we'll do a full reset of the tracked
     // resource arrays.
     static const int kNumRewindResetsBeforeFullReset = 8;
-    int              fNumResets;
+    int              fNumResets = 0;
 
     // Cached values used for dynamic state updates
     VkViewport fCachedViewport;
@@ -190,6 +181,8 @@
     bool fBarriersByRegion = false;
     VkPipelineStageFlags fSrcStageMask = 0;
     VkPipelineStageFlags fDstStageMask = 0;
+
+    bool fIsWrapped;
 };
 
 class GrVkSecondaryCommandBuffer;
@@ -198,17 +191,17 @@
 public:
     ~GrVkPrimaryCommandBuffer() override;
 
-    static GrVkPrimaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool);
+    static GrVkPrimaryCommandBuffer* Create(GrVkGpu* gpu, VkCommandPool cmdPool);
 
-    void begin(const GrVkGpu* gpu);
+    void begin(GrVkGpu* gpu);
     void end(GrVkGpu* gpu);
 
     // Begins render pass on this command buffer. The framebuffer from GrVkRenderTarget will be used
     // in the render pass.
-    void beginRenderPass(const GrVkGpu* gpu,
+    bool beginRenderPass(GrVkGpu* gpu,
                          const GrVkRenderPass* renderPass,
                          const VkClearValue clearValues[],
-                         const GrVkRenderTarget& target,
+                         GrVkRenderTarget* target,
                          const SkIRect& bounds,
                          bool forSecondaryCB);
     void endRenderPass(const GrVkGpu* gpu);
@@ -290,18 +283,18 @@
                       uint32_t regionCount,
                       const VkImageResolve* regions);
 
-    void submitToQueue(const GrVkGpu* gpu, VkQueue queue, GrVkGpu::SyncQueue sync,
+    void submitToQueue(GrVkGpu* gpu, VkQueue queue, GrVkGpu::SyncQueue sync,
                        SkTArray<GrVkSemaphore::Resource*>& signalSemaphores,
                        SkTArray<GrVkSemaphore::Resource*>& waitSemaphores);
     bool finished(const GrVkGpu* gpu);
 
     void addFinishedProc(sk_sp<GrRefCntedCallback> finishedProc);
 
-    void recycleSecondaryCommandBuffers(GrVkGpu* gpu);
+    void recycleSecondaryCommandBuffers(GrVkCommandPool* cmdPool);
 
 private:
-    explicit GrVkPrimaryCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool)
-        : INHERITED(cmdBuffer, cmdPool)
+    explicit GrVkPrimaryCommandBuffer(VkCommandBuffer cmdBuffer)
+        : INHERITED(cmdBuffer)
         , fSubmitFence(VK_NULL_HANDLE) {}
 
     void onFreeGPUData(GrVkGpu* gpu) const override;
@@ -319,26 +312,27 @@
 
 class GrVkSecondaryCommandBuffer : public GrVkCommandBuffer {
 public:
-    static GrVkSecondaryCommandBuffer* Create(const GrVkGpu* gpu, GrVkCommandPool* cmdPool);
+    static GrVkSecondaryCommandBuffer* Create(GrVkGpu* gpu, GrVkCommandPool* cmdPool);
     // Used for wrapping an external secondary command buffer.
     static GrVkSecondaryCommandBuffer* Create(VkCommandBuffer externalSecondaryCB);
 
-    void begin(const GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
+    void begin(GrVkGpu* gpu, const GrVkFramebuffer* framebuffer,
                const GrVkRenderPass* compatibleRenderPass);
     void end(GrVkGpu* gpu);
 
-    void recycle(GrVkGpu* gpu);
+    void recycle(GrVkCommandPool* cmdPool);
 
     VkCommandBuffer vkCommandBuffer() { return fCmdBuffer; }
 
 private:
-    explicit GrVkSecondaryCommandBuffer(VkCommandBuffer cmdBuffer, GrVkCommandPool* cmdPool)
-        : INHERITED(cmdBuffer, cmdPool) {}
+    explicit GrVkSecondaryCommandBuffer(VkCommandBuffer cmdBuffer, bool isWrapped)
+        : INHERITED(cmdBuffer, isWrapped) {}
 
     void onFreeGPUData(GrVkGpu* gpu) const override {}
 
     void onAbandonGPUData() const override {}
 
+    // Used for accessing fIsActive (on GrVkCommandBuffer)
     friend class GrVkPrimaryCommandBuffer;
 
     typedef GrVkCommandBuffer INHERITED;
diff --git a/src/gpu/vk/GrVkCommandPool.cpp b/src/gpu/vk/GrVkCommandPool.cpp
index 5dc5a94..d8adbb5 100644
--- a/src/gpu/vk/GrVkCommandPool.cpp
+++ b/src/gpu/vk/GrVkCommandPool.cpp
@@ -11,7 +11,7 @@
 #include "src/gpu/vk/GrVkCommandBuffer.h"
 #include "src/gpu/vk/GrVkGpu.h"
 
-GrVkCommandPool* GrVkCommandPool::Create(const GrVkGpu* gpu) {
+GrVkCommandPool* GrVkCommandPool::Create(GrVkGpu* gpu) {
     VkCommandPoolCreateFlags cmdPoolCreateFlags =
             VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
             VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
@@ -25,16 +25,26 @@
         cmdPoolCreateFlags,                          // CmdPoolCreateFlags
         gpu->queueIndex(),                           // queueFamilyIndex
     };
+    VkResult result;
     VkCommandPool pool;
-    GR_VK_CALL_ERRCHECK(
-            gpu->vkInterface(),
-            CreateCommandPool(gpu->device(), &cmdPoolInfo, nullptr, &pool));
-    return new GrVkCommandPool(gpu, pool);
+    GR_VK_CALL_RESULT(gpu, result, CreateCommandPool(gpu->device(), &cmdPoolInfo, nullptr, &pool));
+    if (result != VK_SUCCESS) {
+        return nullptr;
+    }
+
+    GrVkPrimaryCommandBuffer* primaryCmdBuffer = GrVkPrimaryCommandBuffer::Create(gpu, pool);
+    if (!primaryCmdBuffer) {
+        GR_VK_CALL(gpu->vkInterface(), DestroyCommandPool(gpu->device(), pool, nullptr));
+        return nullptr;
+    }
+
+    return new GrVkCommandPool(gpu, pool, primaryCmdBuffer);
 }
 
-GrVkCommandPool::GrVkCommandPool(const GrVkGpu* gpu, VkCommandPool commandPool)
-        : fCommandPool(commandPool) {
-    fPrimaryCommandBuffer.reset(GrVkPrimaryCommandBuffer::Create(gpu, this));
+GrVkCommandPool::GrVkCommandPool(GrVkGpu* gpu, VkCommandPool commandPool,
+                                 GrVkPrimaryCommandBuffer* primaryCmdBuffer)
+        : fCommandPool(commandPool)
+        , fPrimaryCommandBuffer(primaryCmdBuffer) {
 }
 
 std::unique_ptr<GrVkSecondaryCommandBuffer> GrVkCommandPool::findOrCreateSecondaryCommandBuffer(
@@ -50,7 +60,6 @@
 }
 
 void GrVkCommandPool::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer) {
-    SkASSERT(buffer->commandPool() == this);
     std::unique_ptr<GrVkSecondaryCommandBuffer> scb(buffer);
     fAvailableSecondaryBuffers.push_back(std::move(scb));
 }
@@ -62,14 +71,19 @@
 void GrVkCommandPool::reset(GrVkGpu* gpu) {
     SkASSERT(!fOpen);
     fOpen = true;
-    fPrimaryCommandBuffer->recycleSecondaryCommandBuffers(gpu);
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), ResetCommandPool(gpu->device(), fCommandPool, 0));
+    // We can't use the normal result macro calls here because we may call reset on a different
+    // thread and we can't be modifying the lost state on the GrVkGpu. We just call
+    // vkResetCommandPool and assume the "next" vulkan call will catch the lost device.
+    SkDEBUGCODE(VkResult result = )GR_VK_CALL(gpu->vkInterface(),
+                                              ResetCommandPool(gpu->device(), fCommandPool, 0));
+    SkASSERT(result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST);
 }
 
 void GrVkCommandPool::releaseResources(GrVkGpu* gpu) {
     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
     SkASSERT(!fOpen);
     fPrimaryCommandBuffer->releaseResources(gpu);
+    fPrimaryCommandBuffer->recycleSecondaryCommandBuffers(this);
 }
 
 void GrVkCommandPool::abandonGPUData() const {
@@ -80,9 +94,15 @@
 }
 
 void GrVkCommandPool::freeGPUData(GrVkGpu* gpu) const {
-    fPrimaryCommandBuffer->freeGPUData(gpu);
+    // TODO: having freeGPUData virtual on GrVkResource be const seems like a bad restriction since
+    // we are changing the internal objects of these classes when it is called. We should go back a
+    // revisit how much of a headache it would be to make this function non-const
+    GrVkCommandPool* nonConstThis = const_cast<GrVkCommandPool*>(this);
+    nonConstThis->close();
+    nonConstThis->releaseResources(gpu);
+    fPrimaryCommandBuffer->freeGPUData(gpu, fCommandPool);
     for (const auto& buffer : fAvailableSecondaryBuffers) {
-        buffer->freeGPUData(gpu);
+        buffer->freeGPUData(gpu, fCommandPool);
     }
     if (fCommandPool != VK_NULL_HANDLE) {
         GR_VK_CALL(gpu->vkInterface(),
diff --git a/src/gpu/vk/GrVkCommandPool.h b/src/gpu/vk/GrVkCommandPool.h
index fd44d62..c90302d 100644
--- a/src/gpu/vk/GrVkCommandPool.h
+++ b/src/gpu/vk/GrVkCommandPool.h
@@ -18,7 +18,7 @@
 
 class GrVkCommandPool : public GrVkResource {
 public:
-    static GrVkCommandPool* Create(const GrVkGpu* gpu);
+    static GrVkCommandPool* Create(GrVkGpu* gpu);
 
     VkCommandPool vkCommandPool() const {
         return fCommandPool;
@@ -50,7 +50,7 @@
 private:
     GrVkCommandPool() = delete;
 
-    GrVkCommandPool(const GrVkGpu* gpu, VkCommandPool commandPool);
+    GrVkCommandPool(GrVkGpu* gpu, VkCommandPool commandPool, GrVkPrimaryCommandBuffer*);
 
     void abandonGPUData() const override;
 
diff --git a/src/gpu/vk/GrVkDescriptorPool.cpp b/src/gpu/vk/GrVkDescriptorPool.cpp
index 16350bd..3d7d9cd 100644
--- a/src/gpu/vk/GrVkDescriptorPool.cpp
+++ b/src/gpu/vk/GrVkDescriptorPool.cpp
@@ -11,10 +11,8 @@
 #include "src/gpu/vk/GrVkGpu.h"
 
 
-GrVkDescriptorPool::GrVkDescriptorPool(const GrVkGpu* gpu, VkDescriptorType type, uint32_t count)
-    : INHERITED()
-    , fType (type)
-    , fCount(count) {
+GrVkDescriptorPool* GrVkDescriptorPool::Create(GrVkGpu* gpu, VkDescriptorType type,
+                                               uint32_t count) {
     VkDescriptorPoolSize poolSize;
     memset(&poolSize, 0, sizeof(VkDescriptorPoolSize));
     poolSize.descriptorCount = count;
@@ -30,20 +28,23 @@
     createInfo.poolSizeCount = 1;
     createInfo.pPoolSizes = &poolSize;
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateDescriptorPool(gpu->device(),
-                                                                 &createInfo,
-                                                                 nullptr,
-                                                                 &fDescPool));
+    VkDescriptorPool pool;
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, CreateDescriptorPool(gpu->device(), &createInfo, nullptr,
+                                                        &pool));
+    if (result != VK_SUCCESS) {
+        return nullptr;
+    }
+    return new GrVkDescriptorPool(pool, type, count);
 }
 
+GrVkDescriptorPool::GrVkDescriptorPool(VkDescriptorPool pool, VkDescriptorType type, uint32_t count)
+        : INHERITED(), fType(type), fCount(count), fDescPool(pool) {}
+
 bool GrVkDescriptorPool::isCompatible(VkDescriptorType type, uint32_t count) const {
     return fType == type && count <= fCount;
 }
 
-void GrVkDescriptorPool::reset(const GrVkGpu* gpu) {
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), ResetDescriptorPool(gpu->device(), fDescPool, 0));
-}
-
 void GrVkDescriptorPool::freeGPUData(GrVkGpu* gpu) const {
     // Destroying the VkDescriptorPool will automatically free and delete any VkDescriptorSets
     // allocated from the pool.
diff --git a/src/gpu/vk/GrVkDescriptorPool.h b/src/gpu/vk/GrVkDescriptorPool.h
index db90e61..b1a199e 100644
--- a/src/gpu/vk/GrVkDescriptorPool.h
+++ b/src/gpu/vk/GrVkDescriptorPool.h
@@ -20,12 +20,10 @@
  */
 class GrVkDescriptorPool : public GrVkResource {
 public:
-    GrVkDescriptorPool(const GrVkGpu* gpu, VkDescriptorType type, uint32_t count);
+    static GrVkDescriptorPool* Create(GrVkGpu* gpu, VkDescriptorType type, uint32_t count);
 
     VkDescriptorPool descPool() const { return fDescPool; }
 
-    void reset(const GrVkGpu* gpu);
-
     // Returns whether or not this descriptor pool could be used, assuming it gets fully reset and
     // not in use by another draw, to support the requested type and count.
     bool isCompatible(VkDescriptorType type, uint32_t count) const;
@@ -38,6 +36,8 @@
 #endif
 
 private:
+    GrVkDescriptorPool(VkDescriptorPool pool, VkDescriptorType type, uint32_t count);
+
     void freeGPUData(GrVkGpu* gpu) const override;
 
     VkDescriptorType     fType;
diff --git a/src/gpu/vk/GrVkDescriptorSet.h b/src/gpu/vk/GrVkDescriptorSet.h
index d909511..7405c07 100644
--- a/src/gpu/vk/GrVkDescriptorSet.h
+++ b/src/gpu/vk/GrVkDescriptorSet.h
@@ -23,7 +23,7 @@
 
     ~GrVkDescriptorSet() override {}
 
-    VkDescriptorSet descriptorSet() const { return fDescSet; }
+    const VkDescriptorSet* descriptorSet() const { return &fDescSet; }
 
 #ifdef SK_TRACE_VK_RESOURCES
     void dumpInfo() const override {
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.cpp b/src/gpu/vk/GrVkDescriptorSetManager.cpp
index 5869ae6..38b33a0 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.cpp
+++ b/src/gpu/vk/GrVkDescriptorSetManager.cpp
@@ -23,10 +23,8 @@
         stages |= kGeometry_GrShaderFlag;
     }
     visibilities.push_back(stages);
-
     SkTArray<const GrVkSampler*> samplers;
-    return new GrVkDescriptorSetManager(gpu, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, visibilities,
-                                        samplers);
+    return Create(gpu, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, visibilities, samplers);
 }
 
 GrVkDescriptorSetManager* GrVkDescriptorSetManager::CreateSamplerManager(
@@ -38,7 +36,7 @@
         visibilities.push_back(uniformHandler.samplerVisibility(i));
         immutableSamplers.push_back(uniformHandler.immutableSampler(i));
     }
-    return new GrVkDescriptorSetManager(gpu, type, visibilities, immutableSamplers);
+    return Create(gpu, type, visibilities, immutableSamplers);
 }
 
 GrVkDescriptorSetManager* GrVkDescriptorSetManager::CreateSamplerManager(
@@ -48,14 +46,119 @@
     for (int i = 0 ; i < visibilities.count(); ++i) {
         immutableSamplers.push_back(nullptr);
     }
-    return new GrVkDescriptorSetManager(gpu, type, visibilities, immutableSamplers);
+    return Create(gpu, type, visibilities, immutableSamplers);
 }
 
-GrVkDescriptorSetManager::GrVkDescriptorSetManager(
+VkShaderStageFlags visibility_to_vk_stage_flags(uint32_t visibility) {
+    VkShaderStageFlags flags = 0;
+
+    if (visibility & kVertex_GrShaderFlag) {
+        flags |= VK_SHADER_STAGE_VERTEX_BIT;
+    }
+    if (visibility & kGeometry_GrShaderFlag) {
+        flags |= VK_SHADER_STAGE_GEOMETRY_BIT;
+    }
+    if (visibility & kFragment_GrShaderFlag) {
+        flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
+    }
+    return flags;
+}
+
+static bool get_layout_and_desc_count(GrVkGpu* gpu,
+                                      VkDescriptorType type,
+                                      const SkTArray<uint32_t>& visibilities,
+                                      const SkTArray<const GrVkSampler*>& immutableSamplers,
+                                      VkDescriptorSetLayout* descSetLayout,
+                                      uint32_t* descCountPerSet) {
+    if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type ||
+        VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type) {
+        uint32_t numBindings = visibilities.count();
+        std::unique_ptr<VkDescriptorSetLayoutBinding[]> dsSamplerBindings(
+                new VkDescriptorSetLayoutBinding[numBindings]);
+        for (uint32_t i = 0; i < numBindings; ++i) {
+            uint32_t visibility = visibilities[i];
+            dsSamplerBindings[i].binding = i;
+            dsSamplerBindings[i].descriptorType = type;
+            dsSamplerBindings[i].descriptorCount = 1;
+            dsSamplerBindings[i].stageFlags = visibility_to_vk_stage_flags(visibility);
+            if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type) {
+                if (immutableSamplers[i]) {
+                    dsSamplerBindings[i].pImmutableSamplers = immutableSamplers[i]->samplerPtr();
+                } else {
+                    dsSamplerBindings[i].pImmutableSamplers = nullptr;
+                }
+            }
+        }
+
+        VkDescriptorSetLayoutCreateInfo dsSamplerLayoutCreateInfo;
+        memset(&dsSamplerLayoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+        dsSamplerLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+        dsSamplerLayoutCreateInfo.pNext = nullptr;
+        dsSamplerLayoutCreateInfo.flags = 0;
+        dsSamplerLayoutCreateInfo.bindingCount = numBindings;
+        // Setting to nullptr fixes an error in the param checker validation layer. Even though
+        // bindingCount is 0 (which is valid), it still tries to validate pBindings unless it is
+        // null.
+        dsSamplerLayoutCreateInfo.pBindings = numBindings ? dsSamplerBindings.get() : nullptr;
+
+#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
+        // skia:8713
+        __lsan::ScopedDisabler lsanDisabler;
+#endif
+        VkResult result;
+        GR_VK_CALL_RESULT(gpu, result,
+                          CreateDescriptorSetLayout(gpu->device(),
+                                                    &dsSamplerLayoutCreateInfo,
+                                                    nullptr,
+                                                    descSetLayout));
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        *descCountPerSet = visibilities.count();
+    } else {
+        SkASSERT(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER == type);
+        static constexpr int kUniformDescPerSet = 1;
+        SkASSERT(kUniformDescPerSet == visibilities.count());
+        // Create Uniform Buffer Descriptor
+        VkDescriptorSetLayoutBinding dsUniBinding;
+        memset(&dsUniBinding, 0, sizeof(dsUniBinding));
+        dsUniBinding.binding = GrVkUniformHandler::kUniformBinding;
+        dsUniBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+        dsUniBinding.descriptorCount = 1;
+        dsUniBinding.stageFlags = visibility_to_vk_stage_flags(visibilities[0]);
+        dsUniBinding.pImmutableSamplers = nullptr;
+
+        VkDescriptorSetLayoutCreateInfo uniformLayoutCreateInfo;
+        memset(&uniformLayoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
+        uniformLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+        uniformLayoutCreateInfo.pNext = nullptr;
+        uniformLayoutCreateInfo.flags = 0;
+        uniformLayoutCreateInfo.bindingCount = 1;
+        uniformLayoutCreateInfo.pBindings = &dsUniBinding;
+
+#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
+        // skia:8713
+        __lsan::ScopedDisabler lsanDisabler;
+#endif
+        VkResult result;
+        GR_VK_CALL_RESULT(gpu, result, CreateDescriptorSetLayout(gpu->device(),
+                                                                 &uniformLayoutCreateInfo,
+                                                                 nullptr,
+                                                                 descSetLayout));
+        if (result != VK_SUCCESS) {
+            return false;
+        }
+
+        *descCountPerSet = kUniformDescPerSet;
+    }
+    return true;
+}
+
+GrVkDescriptorSetManager* GrVkDescriptorSetManager::Create(
         GrVkGpu* gpu, VkDescriptorType type,
         const SkTArray<uint32_t>& visibilities,
-        const SkTArray<const GrVkSampler*>& immutableSamplers)
-    : fPoolManager(type, gpu, visibilities, immutableSamplers) {
+        const SkTArray<const GrVkSampler*>& immutableSamplers) {
 #ifdef SK_DEBUG
     if (type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
         SkASSERT(visibilities.count() == immutableSamplers.count());
@@ -63,6 +166,22 @@
         SkASSERT(immutableSamplers.count() == 0);
     }
 #endif
+
+    VkDescriptorSetLayout descSetLayout;
+    uint32_t descCountPerSet;
+    if (!get_layout_and_desc_count(gpu, type, visibilities, immutableSamplers, &descSetLayout,
+                                   &descCountPerSet)) {
+        return nullptr;
+    }
+    return new GrVkDescriptorSetManager(gpu, type, descSetLayout, descCountPerSet, visibilities,
+                                        immutableSamplers);
+}
+
+GrVkDescriptorSetManager::GrVkDescriptorSetManager(
+        GrVkGpu* gpu, VkDescriptorType type, VkDescriptorSetLayout descSetLayout,
+        uint32_t descCountPerSet, const SkTArray<uint32_t>& visibilities,
+        const SkTArray<const GrVkSampler*>& immutableSamplers)
+    : fPoolManager(descSetLayout, type, descCountPerSet) {
     for (int i = 0; i < visibilities.count(); ++i) {
         fBindingVisibilities.push_back(visibilities[i]);
     }
@@ -84,7 +203,9 @@
         fFreeSets.removeShuffle(count - 1);
     } else {
         VkDescriptorSet vkDS;
-        fPoolManager.getNewDescriptorSet(gpu, &vkDS);
+        if (!fPoolManager.getNewDescriptorSet(gpu, &vkDS)) {
+            return nullptr;
+        }
 
         ds = new GrVkDescriptorSet(vkDS, fPoolManager.fPool, handle);
     }
@@ -171,111 +292,19 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-VkShaderStageFlags visibility_to_vk_stage_flags(uint32_t visibility) {
-    VkShaderStageFlags flags = 0;
-
-    if (visibility & kVertex_GrShaderFlag) {
-        flags |= VK_SHADER_STAGE_VERTEX_BIT;
-    }
-    if (visibility & kGeometry_GrShaderFlag) {
-        flags |= VK_SHADER_STAGE_GEOMETRY_BIT;
-    }
-    if (visibility & kFragment_GrShaderFlag) {
-        flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
-    }
-    return flags;
-}
-
 GrVkDescriptorSetManager::DescriptorPoolManager::DescriptorPoolManager(
+        VkDescriptorSetLayout layout,
         VkDescriptorType type,
-        GrVkGpu* gpu,
-        const SkTArray<uint32_t>& visibilities,
-        const SkTArray<const GrVkSampler*>& immutableSamplers)
-    : fDescType(type)
+        uint32_t descCountPerSet)
+    : fDescLayout(layout)
+    , fDescType(type)
+    , fDescCountPerSet(descCountPerSet)
+    , fMaxDescriptors(kStartNumDescriptors)
     , fCurrentDescriptorCount(0)
     , fPool(nullptr) {
-
-
-    if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type ||
-        VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER == type) {
-        uint32_t numBindings = visibilities.count();
-        std::unique_ptr<VkDescriptorSetLayoutBinding[]> dsSamplerBindings(
-                new VkDescriptorSetLayoutBinding[numBindings]);
-        for (uint32_t i = 0; i < numBindings; ++i) {
-            uint32_t visibility = visibilities[i];
-            dsSamplerBindings[i].binding = i;
-            dsSamplerBindings[i].descriptorType = type;
-            dsSamplerBindings[i].descriptorCount = 1;
-            dsSamplerBindings[i].stageFlags = visibility_to_vk_stage_flags(visibility);
-            if (VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER == type) {
-                if (immutableSamplers[i]) {
-                    dsSamplerBindings[i].pImmutableSamplers = immutableSamplers[i]->samplerPtr();
-                } else {
-                    dsSamplerBindings[i].pImmutableSamplers = nullptr;
-                }
-            }
-        }
-
-        VkDescriptorSetLayoutCreateInfo dsSamplerLayoutCreateInfo;
-        memset(&dsSamplerLayoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
-        dsSamplerLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-        dsSamplerLayoutCreateInfo.pNext = nullptr;
-        dsSamplerLayoutCreateInfo.flags = 0;
-        dsSamplerLayoutCreateInfo.bindingCount = numBindings;
-        // Setting to nullptr fixes an error in the param checker validation layer. Even though
-        // bindingCount is 0 (which is valid), it still tries to validate pBindings unless it is
-        // null.
-        dsSamplerLayoutCreateInfo.pBindings = numBindings ? dsSamplerBindings.get() : nullptr;
-
-#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
-        // skia:8713
-        __lsan::ScopedDisabler lsanDisabler;
-#endif
-        GR_VK_CALL_ERRCHECK(gpu->vkInterface(),
-                            CreateDescriptorSetLayout(gpu->device(),
-                                                      &dsSamplerLayoutCreateInfo,
-                                                      nullptr,
-                                                      &fDescLayout));
-        fDescCountPerSet = visibilities.count();
-    } else {
-        SkASSERT(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER == type);
-        GR_STATIC_ASSERT(1 == kUniformDescPerSet);
-        SkASSERT(kUniformDescPerSet == visibilities.count());
-        // Create Uniform Buffer Descriptor
-        VkDescriptorSetLayoutBinding dsUniBinding;
-        memset(&dsUniBinding, 0, sizeof(dsUniBinding));
-        dsUniBinding.binding = GrVkUniformHandler::kUniformBinding;
-        dsUniBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
-        dsUniBinding.descriptorCount = 1;
-        dsUniBinding.stageFlags = visibility_to_vk_stage_flags(visibilities[0]);
-        dsUniBinding.pImmutableSamplers = nullptr;
-
-        VkDescriptorSetLayoutCreateInfo uniformLayoutCreateInfo;
-        memset(&uniformLayoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
-        uniformLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
-        uniformLayoutCreateInfo.pNext = nullptr;
-        uniformLayoutCreateInfo.flags = 0;
-        uniformLayoutCreateInfo.bindingCount = 1;
-        uniformLayoutCreateInfo.pBindings = &dsUniBinding;
-
-#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
-        // skia:8713
-        __lsan::ScopedDisabler lsanDisabler;
-#endif
-        GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateDescriptorSetLayout(gpu->device(),
-                                                                          &uniformLayoutCreateInfo,
-                                                                          nullptr,
-                                                                          &fDescLayout));
-        fDescCountPerSet = kUniformDescPerSet;
-    }
-
-    SkASSERT(fDescCountPerSet < kStartNumDescriptors);
-    fMaxDescriptors = kStartNumDescriptors;
-    SkASSERT(fMaxDescriptors > 0);
-    this->getNewPool(gpu);
 }
 
-void GrVkDescriptorSetManager::DescriptorPoolManager::getNewPool(GrVkGpu* gpu) {
+bool GrVkDescriptorSetManager::DescriptorPoolManager::getNewPool(GrVkGpu* gpu) {
     if (fPool) {
         fPool->unref(gpu);
         uint32_t newPoolSize = fMaxDescriptors + ((fMaxDescriptors + 1) >> 1);
@@ -288,17 +317,19 @@
     }
     fPool = gpu->resourceProvider().findOrCreateCompatibleDescriptorPool(fDescType,
                                                                          fMaxDescriptors);
-    SkASSERT(fPool);
+    return SkToBool(fPool);
 }
 
-void GrVkDescriptorSetManager::DescriptorPoolManager::getNewDescriptorSet(GrVkGpu* gpu,
+bool GrVkDescriptorSetManager::DescriptorPoolManager::getNewDescriptorSet(GrVkGpu* gpu,
                                                                           VkDescriptorSet* ds) {
     if (!fMaxDescriptors) {
-        return;
+        return false;
     }
     fCurrentDescriptorCount += fDescCountPerSet;
-    if (fCurrentDescriptorCount > fMaxDescriptors) {
-        this->getNewPool(gpu);
+    if (!fPool || fCurrentDescriptorCount > fMaxDescriptors) {
+        if (!this->getNewPool(gpu) ) {
+            return false;
+        }
         fCurrentDescriptorCount = fDescCountPerSet;
     }
 
@@ -309,9 +340,11 @@
     dsAllocateInfo.descriptorPool = fPool->descPool();
     dsAllocateInfo.descriptorSetCount = 1;
     dsAllocateInfo.pSetLayouts = &fDescLayout;
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), AllocateDescriptorSets(gpu->device(),
-                                                                   &dsAllocateInfo,
-                                                                   ds));
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, AllocateDescriptorSets(gpu->device(),
+                                                          &dsAllocateInfo,
+                                                          ds));
+    return result == VK_SUCCESS;
 }
 
 void GrVkDescriptorSetManager::DescriptorPoolManager::freeGPUResources(GrVkGpu* gpu) {
diff --git a/src/gpu/vk/GrVkDescriptorSetManager.h b/src/gpu/vk/GrVkDescriptorSetManager.h
index 767ca33..e4faf06 100644
--- a/src/gpu/vk/GrVkDescriptorSetManager.h
+++ b/src/gpu/vk/GrVkDescriptorSetManager.h
@@ -50,17 +50,15 @@
 
 private:
     struct DescriptorPoolManager {
-        DescriptorPoolManager(VkDescriptorType type, GrVkGpu* gpu,
-                              const SkTArray<uint32_t>& visibilities,
-                              const SkTArray<const GrVkSampler*>& immutableSamplers);
-
+        DescriptorPoolManager(VkDescriptorSetLayout, VkDescriptorType type,
+                              uint32_t descCountPerSet);
 
         ~DescriptorPoolManager() {
             SkASSERT(!fDescLayout);
             SkASSERT(!fPool);
         }
 
-        void getNewDescriptorSet(GrVkGpu* gpu, VkDescriptorSet* ds);
+        bool getNewDescriptorSet(GrVkGpu* gpu, VkDescriptorSet* ds);
 
         void freeGPUResources(GrVkGpu* gpu);
         void abandonGPUResources();
@@ -74,16 +72,20 @@
 
     private:
         enum {
-            kUniformDescPerSet = 1,
             kMaxDescriptors = 1024,
             kStartNumDescriptors = 16, // must be less than kMaxUniformDescriptors
         };
 
-        void getNewPool(GrVkGpu* gpu);
+        bool getNewPool(GrVkGpu* gpu);
     };
 
+    static GrVkDescriptorSetManager* Create(GrVkGpu* gpu,
+                                            VkDescriptorType,
+                                            const SkTArray<uint32_t>& visibilities,
+                                            const SkTArray<const GrVkSampler*>& immutableSamplers);
+
     GrVkDescriptorSetManager(GrVkGpu* gpu,
-                             VkDescriptorType,
+                             VkDescriptorType, VkDescriptorSetLayout, uint32_t descCountPerSet,
                              const SkTArray<uint32_t>& visibilities,
                              const SkTArray<const GrVkSampler*>& immutableSamplers);
 
diff --git a/src/gpu/vk/GrVkFramebuffer.cpp b/src/gpu/vk/GrVkFramebuffer.cpp
index a06d792..f256999 100644
--- a/src/gpu/vk/GrVkFramebuffer.cpp
+++ b/src/gpu/vk/GrVkFramebuffer.cpp
@@ -40,10 +40,9 @@
     createInfo.layers = 1;
 
     VkFramebuffer framebuffer;
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateFramebuffer(gpu->device(),
-                                                                    &createInfo,
-                                                                    nullptr,
-                                                                    &framebuffer));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, CreateFramebuffer(gpu->device(), &createInfo, nullptr,
+                                                  &framebuffer));
     if (err) {
         return nullptr;
     }
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index d9baea4..db65b40a 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -60,8 +60,7 @@
 #endif
 
 #define VK_CALL(X) GR_VK_CALL(this->vkInterface(), X)
-#define VK_CALL_RET(RET, X) GR_VK_CALL_RET(this->vkInterface(), RET, X)
-#define VK_CALL_ERRCHECK(X) GR_VK_CALL_ERRCHECK(this->vkInterface(), X)
+#define VK_CALL_RET(RET, X) GR_VK_CALL_RESULT(this, RET, X)
 
 sk_sp<GrGpu> GrVkGpu::Make(const GrVkBackendContext& backendContext,
                            const GrContextOptions& options, GrContext* context) {
@@ -323,12 +322,14 @@
             GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
             const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
             const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     if (!fCachedOpsRenderPass) {
         fCachedOpsRenderPass.reset(new GrVkOpsRenderPass(this));
     }
 
-    fCachedOpsRenderPass->set(rt, origin, bounds, colorInfo, stencilInfo, sampledProxies);
+    if (!fCachedOpsRenderPass->set(rt, origin, bounds, colorInfo, stencilInfo, sampledProxies)) {
+        return nullptr;
+    }
     return fCachedOpsRenderPass.get();
 }
 
@@ -1433,18 +1434,16 @@
 
 GrStencilAttachment* GrVkGpu::createStencilAttachmentForRenderTarget(
         const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
-    SkASSERT(numStencilSamples == rt->numSamples());
+    SkASSERT(numStencilSamples == rt->numSamples() || this->caps()->mixedSamplesSupport());
     SkASSERT(width >= rt->width());
     SkASSERT(height >= rt->height());
 
-    int samples = rt->numSamples();
-
     const GrVkCaps::StencilFormat& sFmt = this->vkCaps().preferredStencilFormat();
 
     GrVkStencilAttachment* stencil(GrVkStencilAttachment::Create(this,
                                                                  width,
                                                                  height,
-                                                                 samples,
+                                                                 numStencilSamples,
                                                                  sFmt));
     fStats.incStencilAttachmentCreates();
     return stencil;
@@ -1498,26 +1497,27 @@
     barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     barrier.image = info->fImage;
     barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, mipLevels, 0, 1};
-    GR_VK_CALL(vkInterface, CmdPipelineBarrier(
-                               cmdBuffer,
-                               srcStageMask,
-                               dstStageMask,
-                               0,
-                               0, nullptr,
-                               0, nullptr,
-                               1, &barrier));
+    GR_VK_CALL(vkInterface, CmdPipelineBarrier(cmdBuffer,
+                                               srcStageMask,
+                                               dstStageMask,
+                                               0,
+                                               0, nullptr,
+                                               0, nullptr,
+                                               1, &barrier));
     info->fImageLayout = newLayout;
 }
 
-bool GrVkGpu::createVkImageForBackendSurface(VkFormat vkFormat, int w, int h, bool texturable,
-                                             bool renderable, GrMipMapped mipMapped,
-                                             const SkPixmap srcData[], int numMipLevels,
-                                             const SkColor4f* color, GrVkImageInfo* info,
+bool GrVkGpu::createVkImageForBackendSurface(VkFormat vkFormat,
+                                             SkISize dimensions,
+                                             bool texturable,
+                                             bool renderable,
+                                             const BackendTextureData* data,
+                                             int numMipLevels,
+                                             GrVkImageInfo* info,
                                              GrProtected isProtected) {
     SkASSERT(texturable || renderable);
     if (!texturable) {
-        SkASSERT(GrMipMapped::kNo == mipMapped);
-        SkASSERT(!srcData && !numMipLevels);
+        SkASSERT(!data && numMipLevels == 1);
     }
 
     // Compressed formats go through onCreateCompressedBackendTexture
@@ -1545,21 +1545,12 @@
         usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
     }
 
-    // Figure out the number of mip levels.
-    uint32_t mipLevelCount = 1;
-    if (srcData) {
-        SkASSERT(numMipLevels > 0);
-        mipLevelCount = numMipLevels;
-    } else if (GrMipMapped::kYes == mipMapped) {
-        mipLevelCount = SkMipMap::ComputeLevelCount(w, h) + 1;
-    }
-
     GrVkImage::ImageDesc imageDesc;
     imageDesc.fImageType = VK_IMAGE_TYPE_2D;
     imageDesc.fFormat = vkFormat;
-    imageDesc.fWidth = w;
-    imageDesc.fHeight = h;
-    imageDesc.fLevels = mipLevelCount;
+    imageDesc.fWidth = dimensions.width();
+    imageDesc.fHeight = dimensions.height();
+    imageDesc.fLevels = numMipLevels;
     imageDesc.fSamples = 1;
     imageDesc.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
     imageDesc.fUsageFlags = usageFlags;
@@ -1571,7 +1562,7 @@
         return false;
     }
 
-    if (!srcData && !color) {
+    if (!data) {
         return true;
     }
 
@@ -1590,7 +1581,7 @@
     };
 
     VkCommandBuffer cmdBuffer;
-    err = VK_CALL(AllocateCommandBuffers(fDevice, &cmdInfo, &cmdBuffer));
+    VK_CALL_RET(err, AllocateCommandBuffers(fDevice, &cmdInfo, &cmdBuffer));
     if (err) {
         GrVkImage::DestroyImageInfo(this, info);
         return false;
@@ -1603,22 +1594,25 @@
     cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
     cmdBufferBeginInfo.pInheritanceInfo = nullptr;
 
-    err = VK_CALL(BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
-    SkASSERT(!err);
+    VK_CALL_RET(err, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
+    if (err) {
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
+        GrVkImage::DestroyImageInfo(this, info);
+        return false;
+    }
 
     // Set image layout and add barrier
     set_image_layout(this->vkInterface(), cmdBuffer, info, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
-                     mipLevelCount, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+                     numMipLevels, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
 
-    if (srcData) {
+    if (data->type() == BackendTextureData::Type::kPixmaps) {
         size_t bytesPerPixel = fVkCaps->bytesPerPixel(vkFormat);
-        SkASSERT(w && h);
+        SkASSERT(!dimensions.isEmpty());
 
-        SkTArray<size_t> individualMipOffsets(mipLevelCount);
+        SkTArray<size_t> individualMipOffsets(numMipLevels);
 
-        size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bytesPerPixel, w, h,
-                                                                     &individualMipOffsets,
-                                                                     mipLevelCount);
+        size_t combinedBufferSize = GrComputeTightCombinedBufferSize(
+                bytesPerPixel, dimensions, &individualMipOffsets, numMipLevels);
 
         VkBufferCreateInfo bufInfo;
         memset(&bufInfo, 0, sizeof(VkBufferCreateInfo));
@@ -1629,7 +1623,7 @@
         bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
         bufInfo.queueFamilyIndexCount = 0;
         bufInfo.pQueueFamilyIndices = nullptr;
-        err = VK_CALL(CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
+        VK_CALL_RET(err, CreateBuffer(fDevice, &bufInfo, nullptr, &buffer));
 
         if (err) {
             GrVkImage::DestroyImageInfo(this, info);
@@ -1648,7 +1642,7 @@
         }
 
         bool result = copy_src_data(this, bufferAlloc, vkFormat, individualMipOffsets,
-                                    srcData, numMipLevels);
+                                    data->pixmaps(), numMipLevels);
         if (!result) {
             GrVkImage::DestroyImageInfo(this, info);
             GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
@@ -1658,66 +1652,73 @@
             return false;
         }
 
-        SkTArray<VkBufferImageCopy> regions(mipLevelCount);
+        SkTArray<VkBufferImageCopy> regions(numMipLevels);
 
-        int currentWidth = w;
-        int currentHeight = h;
-        for (uint32_t currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
+        SkISize levelDimensions = dimensions;
+        for (int i = 0; i < numMipLevels; ++i) {
             // Submit copy command
             VkBufferImageCopy& region = regions.push_back();
             memset(&region, 0, sizeof(VkBufferImageCopy));
-            region.bufferOffset = individualMipOffsets[currentMipLevel];
-            region.bufferRowLength = currentWidth;
-            region.bufferImageHeight = currentHeight;
-            region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, currentMipLevel, 0, 1};
+            region.bufferOffset = individualMipOffsets[i];
+            region.bufferRowLength = levelDimensions.width();
+            region.bufferImageHeight = levelDimensions.height();
+            region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, SkToU32(i), 0, 1};
             region.imageOffset = {0, 0, 0};
-            region.imageExtent = {(uint32_t)currentWidth, (uint32_t)currentHeight, 1};
-            currentWidth = SkTMax(1, currentWidth / 2);
-            currentHeight = SkTMax(1, currentHeight / 2);
+            region.imageExtent = {SkToU32(levelDimensions.width()),
+                                  SkToU32(levelDimensions.height()), 1};
+
+            levelDimensions = {SkTMax(1, levelDimensions.width() /2),
+                               SkTMax(1, levelDimensions.height()/2)};
         }
 
         VK_CALL(CmdCopyBufferToImage(cmdBuffer, buffer, info->fImage, info->fImageLayout,
                                      regions.count(), regions.begin()));
     } else {
-        SkASSERT(color);
+        SkASSERT(data->type() == BackendTextureData::Type::kColor);
         VkClearColorValue vkColor;
+        SkColor4f color = data->color();
         // If we ever support SINT or UINT formats this needs to be updated to use the int32 and
         // uint32 union members in those cases.
-        vkColor.float32[0] = color->fR;
-        vkColor.float32[1] = color->fG;
-        vkColor.float32[2] = color->fB;
-        vkColor.float32[3] = color->fA;
+        vkColor.float32[0] = color.fR;
+        vkColor.float32[1] = color.fG;
+        vkColor.float32[2] = color.fB;
+        vkColor.float32[3] = color.fA;
         VkImageSubresourceRange range;
         range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
         range.baseArrayLayer = 0;
         range.baseMipLevel = 0;
         range.layerCount = 1;
-        range.levelCount = mipLevelCount;
+        range.levelCount = numMipLevels;
         VK_CALL(CmdClearColorImage(cmdBuffer, info->fImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                    &vkColor, 1, &range));
     }
 
-    if (!srcData && renderable) {
-        SkASSERT(color);
-
+    if (data->type() == BackendTextureData::Type::kColor && renderable) {
         // Change image layout to color-attachment-optimal since if we use this texture as a
         // borrowed texture within Ganesh we are probably going to render to it
         set_image_layout(this->vkInterface(), cmdBuffer, info,
-                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, mipLevelCount,
-                         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
-                         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, numMipLevels,
+                         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
                          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
     } else if (texturable) {
         // Change image layout to shader read since if we use this texture as a borrowed
         // texture within Ganesh we require that its layout be set to that
         set_image_layout(this->vkInterface(), cmdBuffer, info,
-                         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, mipLevelCount,
+                         VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, numMipLevels,
                          VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
     }
 
     // End CommandBuffer
-    err = VK_CALL(EndCommandBuffer(cmdBuffer));
-    SkASSERT(!err);
+    VK_CALL_RET(err, EndCommandBuffer(cmdBuffer));
+    if (err) {
+        GrVkImage::DestroyImageInfo(this, info);
+        if (buffer != VK_NULL_HANDLE) { // workaround for an older NVidia driver crash
+            GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
+            VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+        }
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
+        return false;
+    }
 
     // Create Fence for queue
     VkFenceCreateInfo fenceInfo;
@@ -1727,8 +1728,16 @@
     fenceInfo.flags = 0;
     VkFence fence = VK_NULL_HANDLE;
 
-    err = VK_CALL(CreateFence(fDevice, &fenceInfo, nullptr, &fence));
-    SkASSERT(!err);
+    VK_CALL_RET(err, CreateFence(fDevice, &fenceInfo, nullptr, &fence));
+    if (err) {
+        GrVkImage::DestroyImageInfo(this, info);
+        if (buffer != VK_NULL_HANDLE) { // workaround for an older NVidia driver crash
+            GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
+            VK_CALL(DestroyBuffer(fDevice, buffer, nullptr));
+        }
+        VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
+        return false;
+    }
 
     VkProtectedSubmitInfo protectedSubmitInfo;
     if (fProtectedContext == GrProtected::kYes) {
@@ -1749,11 +1758,12 @@
     submitInfo.pCommandBuffers = &cmdBuffer;
     submitInfo.signalSemaphoreCount = 0;
     submitInfo.pSignalSemaphores = nullptr;
-    err = VK_CALL(QueueSubmit(this->queue(), 1, &submitInfo, fence));
-    SkASSERT(!err);
+    VK_CALL_RET(err, QueueSubmit(this->queue(), 1, &submitInfo, fence));
 
-    err = VK_CALL(WaitForFences(this->device(), 1, &fence, VK_TRUE, UINT64_MAX));
-    if (VK_TIMEOUT == err) {
+    if (!err) {
+        VK_CALL_RET(err, WaitForFences(this->device(), 1, &fence, VK_TRUE, UINT64_MAX));
+    }
+    if (VK_SUCCESS != err) {
         GrVkImage::DestroyImageInfo(this, info);
         if (buffer != VK_NULL_HANDLE) { // workaround for an older NVidia driver crash
             GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
@@ -1761,10 +1771,14 @@
         }
         VK_CALL(FreeCommandBuffers(fDevice, fCmdPool->vkCommandPool(), 1, &cmdBuffer));
         VK_CALL(DestroyFence(this->device(), fence, nullptr));
-        SkDebugf("Fence failed to signal: %d\n", err);
-        SK_ABORT("failing");
+        if (VK_TIMEOUT == err) {
+            SkDebugf("Fence failed to signal: %d\n", err);
+            SK_ABORT("failing");
+        } else {
+            SkDebugf("Queue submit or fence wait failed: %d\n", err);
+            return false;
+        }
     }
-    SkASSERT(!err);
 
     // Clean up transfer resources
     if (buffer != VK_NULL_HANDLE) { // workaround for an older NVidia driver crash
@@ -1777,21 +1791,16 @@
     return true;
 }
 
-GrBackendTexture GrVkGpu::onCreateBackendTexture(int w, int h,
+GrBackendTexture GrVkGpu::onCreateBackendTexture(SkISize dimensions,
                                                  const GrBackendFormat& format,
-                                                 GrMipMapped mipMapped,
                                                  GrRenderable renderable,
-                                                 const SkPixmap srcData[], int numMipLevels,
-                                                 const SkColor4f* color, GrProtected isProtected) {
+                                                 const BackendTextureData* data,
+                                                 int numMipLevels,
+                                                 GrProtected isProtected) {
     this->handleDirtyContext();
 
     const GrVkCaps& caps = this->vkCaps();
 
-    // GrGpu::createBackendTexture should've ensured these conditions
-    SkASSERT(w >= 1 && w <= caps.maxTextureSize() && h >= 1 && h <= caps.maxTextureSize());
-    SkASSERT(GrGpu::MipMapsAreCorrect(w, h, mipMapped, srcData, numMipLevels));
-    SkASSERT(mipMapped == GrMipMapped::kNo || caps.mipMapSupport());
-
     if (fProtectedContext != isProtected) {
         return GrBackendTexture();
     }
@@ -1814,14 +1823,67 @@
     }
 
     GrVkImageInfo info;
-    if (!this->createVkImageForBackendSurface(vkFormat, w, h, true,
-                                              GrRenderable::kYes == renderable, mipMapped,
-                                              srcData, numMipLevels, color, &info, isProtected)) {
+    if (!this->createVkImageForBackendSurface(vkFormat, dimensions, true,
+                                              GrRenderable::kYes == renderable, data, numMipLevels,
+                                              &info, isProtected)) {
         SkDebugf("Failed to create testing only image\n");
         return GrBackendTexture();
     }
 
-    return GrBackendTexture(w, h, info);
+    return GrBackendTexture(dimensions.width(), dimensions.height(), info);
+}
+
+void GrVkGpu::querySampleLocations(GrRenderTarget* renderTarget,
+                                   SkTArray<SkPoint>* sampleLocations) {
+    // In Vulkan, sampleLocationsSupport() means that the platform uses the standard sample
+    // locations defined by the spec.
+    SkASSERT(this->caps()->sampleLocationsSupport());
+    static constexpr SkPoint kStandardSampleLocations_1[1] = {
+        {0.5f, 0.5f}};
+    static constexpr SkPoint kStandardSampleLocations_2[2] = {
+        {0.75f, 0.75f}, {0.25f, 0.25f}};
+    static constexpr SkPoint kStandardSampleLocations_4[4] = {
+        {0.375f, 0.125f}, {0.875f, 0.375f}, {0.125f, 0.625f}, {0.625f, 0.875f}};
+    static constexpr SkPoint kStandardSampleLocations_8[8] = {
+        {0.5625f, 0.3125f}, {0.4375f, 0.6875f}, {0.8125f, 0.5625f}, {0.3125f, 0.1875f},
+        {0.1875f, 0.8125f}, {0.0625f, 0.4375f}, {0.6875f, 0.9375f}, {0.9375f, 0.0625f}};
+    static constexpr SkPoint kStandardSampleLocations_16[16] = {
+        {0.5625f, 0.5625f}, {0.4375f, 0.3125f}, {0.3125f, 0.625f}, {0.75f, 0.4375f},
+        {0.1875f, 0.375f}, {0.625f, 0.8125f}, {0.8125f, 0.6875f}, {0.6875f, 0.1875f},
+        {0.375f, 0.875f}, {0.5f, 0.0625f}, {0.25f, 0.125f}, {0.125f, 0.75f},
+        {0.0f, 0.5f}, {0.9375f, 0.25f}, {0.875f, 0.9375f}, {0.0625f, 0.0f}};
+
+    int numSamples = renderTarget->numSamples();
+    if (1 == numSamples) {
+        SkASSERT(this->caps()->mixedSamplesSupport());
+        if (auto* stencil = renderTarget->renderTargetPriv().getStencilAttachment()) {
+            numSamples = stencil->numSamples();
+        }
+    }
+    SkASSERT(numSamples > 1);
+    SkASSERT(!renderTarget->renderTargetPriv().getStencilAttachment() ||
+             numSamples == renderTarget->renderTargetPriv().getStencilAttachment()->numSamples());
+
+    switch (numSamples) {
+        case 1:
+            sampleLocations->push_back_n(1, kStandardSampleLocations_1);
+            break;
+        case 2:
+            sampleLocations->push_back_n(2, kStandardSampleLocations_2);
+            break;
+        case 4:
+            sampleLocations->push_back_n(4, kStandardSampleLocations_4);
+            break;
+        case 8:
+            sampleLocations->push_back_n(8, kStandardSampleLocations_8);
+            break;
+        case 16:
+            sampleLocations->push_back_n(16, kStandardSampleLocations_16);
+            break;
+        default:
+            SK_ABORT("Invalid vulkan sample count.");
+            break;
+    }
 }
 
 void GrVkGpu::deleteBackendTexture(const GrBackendTexture& tex) {
@@ -1866,8 +1928,7 @@
     VkFormat vkFormat = this->vkCaps().getFormatFromColorType(ct);
 
     GrVkImageInfo info;
-    if (!this->createVkImageForBackendSurface(vkFormat, w, h, false, true, GrMipMapped::kNo,
-                                              nullptr, 0, &SkColors::kTransparent, &info,
+    if (!this->createVkImageForBackendSurface(vkFormat, {w, h}, false, true, nullptr, 1, &info,
                                               GrProtected::kNo)) {
         return {};
     }
@@ -1925,7 +1986,7 @@
                                        barrier);
 }
 
-void GrVkGpu::onFinishFlush(GrSurfaceProxy* proxies[], int n,
+bool GrVkGpu::onFinishFlush(GrSurfaceProxy* proxies[], int n,
                             SkSurface::BackendSurfaceAccess access, const GrFlushInfo& info,
                             const GrPrepareForExternalIORequests& externalRequests) {
     SkASSERT(n >= 0);
@@ -2002,6 +2063,9 @@
     } else {
         this->submitCommandBuffer(kSkip_SyncQueue, info.fFinishedProc, info.fFinishedContext);
     }
+    // TODO: We may fail to wait on fences or submit command buffers. We should return false here if
+    // we fail any part of the submission.
+    return true;
 }
 
 static int get_surface_sample_cnt(GrSurface* surf) {
@@ -2401,7 +2465,7 @@
     }
 }
 
-void GrVkGpu::beginRenderPass(const GrVkRenderPass* renderPass,
+bool GrVkGpu::beginRenderPass(const GrVkRenderPass* renderPass,
                               const VkClearValue* colorClear,
                               GrVkRenderTarget* target, GrSurfaceOrigin origin,
                               const SkIRect& bounds, bool forSecondaryCB) {
@@ -2434,8 +2498,8 @@
     clears[1].depthStencil.depth = 0.0f;
     clears[1].depthStencil.stencil = 0;
 
-    fCurrentCmdBuffer->beginRenderPass(this, renderPass, clears, *target, adjustedBounds,
-                                       forSecondaryCB);
+   return fCurrentCmdBuffer->beginRenderPass(this, renderPass, clears, target, adjustedBounds,
+                                             forSecondaryCB);
 }
 
 void GrVkGpu::endRenderPass(GrRenderTarget* target, GrSurfaceOrigin origin,
@@ -2462,9 +2526,17 @@
     createInfo.pNext = nullptr;
     createInfo.flags = 0;
     VkFence fence = VK_NULL_HANDLE;
+    VkResult result;
 
-    VK_CALL_ERRCHECK(CreateFence(this->device(), &createInfo, nullptr, &fence));
-    VK_CALL(QueueSubmit(this->queue(), 0, nullptr, fence));
+    VK_CALL_RET(result, CreateFence(this->device(), &createInfo, nullptr, &fence));
+    if (result != VK_SUCCESS) {
+        return 0;
+    }
+    VK_CALL_RET(result, QueueSubmit(this->queue(), 0, nullptr, fence));
+    if (result != VK_SUCCESS) {
+        VK_CALL(DestroyFence(this->device(), fence, nullptr));
+        return 0;
+    }
 
     GR_STATIC_ASSERT(sizeof(GrFence) >= sizeof(VkFence));
     return (GrFence)fence;
@@ -2473,7 +2545,8 @@
 bool GrVkGpu::waitFence(GrFence fence, uint64_t timeout) {
     SkASSERT(VK_NULL_HANDLE != (VkFence)fence);
 
-    VkResult result = VK_CALL(WaitForFences(this->device(), 1, (VkFence*)&fence, VK_TRUE, timeout));
+    VkResult result;
+    VK_CALL_RET(result, WaitForFences(this->device(), 1, (VkFence*)&fence, VK_TRUE, timeout));
     return (VK_SUCCESS == result);
 }
 
@@ -2481,18 +2554,19 @@
     VK_CALL(DestroyFence(this->device(), (VkFence)fence, nullptr));
 }
 
-sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore(bool isOwned) {
+std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore(bool isOwned) {
     return GrVkSemaphore::Make(this, isOwned);
 }
 
-sk_sp<GrSemaphore> GrVkGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                                 GrResourceProvider::SemaphoreWrapType wrapType,
-                                                 GrWrapOwnership ownership) {
+std::unique_ptr<GrSemaphore> GrVkGpu::wrapBackendSemaphore(
+        const GrBackendSemaphore& semaphore,
+        GrResourceProvider::SemaphoreWrapType wrapType,
+        GrWrapOwnership ownership) {
     return GrVkSemaphore::MakeWrapped(this, semaphore.vkSemaphore(), wrapType, ownership);
 }
 
-void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore) {
-    GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore.get());
+void GrVkGpu::insertSemaphore(GrSemaphore* semaphore) {
+    GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore);
 
     GrVkSemaphore::Resource* resource = vkSem->getResource();
     if (resource->shouldSignal()) {
@@ -2501,8 +2575,8 @@
     }
 }
 
-void GrVkGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) {
-    GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore.get());
+void GrVkGpu::waitSemaphore(GrSemaphore* semaphore) {
+    GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore);
 
     GrVkSemaphore::Resource* resource = vkSem->getResource();
     if (resource->shouldWait()) {
@@ -2511,7 +2585,7 @@
     }
 }
 
-sk_sp<GrSemaphore> GrVkGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
+std::unique_ptr<GrSemaphore> GrVkGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
     SkASSERT(texture);
     GrVkTexture* vkTexture = static_cast<GrVkTexture*>(texture);
     vkTexture->setImageLayout(this,
@@ -2533,24 +2607,6 @@
     fDrawables.emplace_back(std::move(drawable));
 }
 
-uint32_t GrVkGpu::getExtraSamplerKeyForProgram(const GrSamplerState& samplerState,
-                                               const GrBackendFormat& format) {
-    const GrVkYcbcrConversionInfo* ycbcrInfo = format.getVkYcbcrConversionInfo();
-    SkASSERT(ycbcrInfo);
-    if (!ycbcrInfo->isValid()) {
-        return 0;
-    }
-
-    const GrVkSampler* sampler = this->resourceProvider().findOrCreateCompatibleSampler(
-            samplerState, *ycbcrInfo);
-
-    uint32_t result = sampler->uniqueID();
-
-    sampler->unref(this);
-
-    return result;
-}
-
 void GrVkGpu::storeVkPipelineCacheData() {
     if (this->getContext()->priv().getPersistentCache()) {
         this->resourceProvider().storePipelineCacheData();
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index cdbb31c..69f418c 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -48,6 +48,9 @@
     const GrVkInterface* vkInterface() const { return fInterface.get(); }
     const GrVkCaps& vkCaps() const { return *fVkCaps; }
 
+    bool isDeviceLost() const { return fDeviceIsLost; }
+    void setDeviceLost() { fDeviceIsLost = true; }
+
     GrVkMemoryAllocator* memoryAllocator() const { return fMemoryAllocator.get(); }
 
     VkPhysicalDevice physicalDevice() const { return fPhysicalDevice; }
@@ -72,10 +75,7 @@
         kSkip_SyncQueue
     };
 
-    void querySampleLocations(GrRenderTarget*, SkTArray<SkPoint>*) override {
-        SkASSERT(!this->caps()->sampleLocationsSupport());
-        SK_ABORT("Sample locations not yet implemented for Vulkan.");
-    }
+    void querySampleLocations(GrRenderTarget*, SkTArray<SkPoint>*) override;
 
     void xferBarrier(GrRenderTarget*, GrXferBarrierType) override {}
 
@@ -100,7 +100,7 @@
             GrRenderTarget*, GrSurfaceOrigin, const SkIRect&,
             const GrOpsRenderPass::LoadAndStoreInfo&,
             const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-            const SkTArray<GrTextureProxy*, true>& sampledProxies) override;
+            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) override;
 
     void addBufferMemoryBarrier(const GrVkResource*,
                                 VkPipelineStageFlags srcStageMask,
@@ -130,12 +130,11 @@
     bool waitFence(GrFence, uint64_t timeout) override;
     void deleteFence(GrFence) const override;
 
-    sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
-    sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
-                                            GrResourceProvider::SemaphoreWrapType wrapType,
-                                            GrWrapOwnership ownership) override;
-    void insertSemaphore(sk_sp<GrSemaphore> semaphore) override;
-    void waitSemaphore(sk_sp<GrSemaphore> semaphore) override;
+    std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override;
+    std::unique_ptr<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
+            GrResourceProvider::SemaphoreWrapType wrapType, GrWrapOwnership ownership) override;
+    void insertSemaphore(GrSemaphore* semaphore) override;
+    void waitSemaphore(GrSemaphore* semaphore) override;
 
     // These match the definitions in SkDrawable, from whence they came
     typedef void* SubmitContext;
@@ -147,15 +146,12 @@
 
     void checkFinishProcs() override { fResourceProvider.checkCommandBuffers(); }
 
-    sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
+    std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
 
     void copyBuffer(GrVkBuffer* srcBuffer, GrVkBuffer* dstBuffer, VkDeviceSize srcOffset,
                     VkDeviceSize dstOffset, VkDeviceSize size);
     bool updateBuffer(GrVkBuffer* buffer, const void* src, VkDeviceSize offset, VkDeviceSize size);
 
-    uint32_t getExtraSamplerKeyForProgram(const GrSamplerState&,
-                                          const GrBackendFormat& format) override;
-
     enum PersistentCacheKeyType : uint32_t {
         kShader_PersistentCacheKeyType = 0,
         kPipelineCache_PersistentCacheKeyType = 1,
@@ -163,7 +159,7 @@
 
     void storeVkPipelineCacheData() override;
 
-    void beginRenderPass(const GrVkRenderPass*,
+    bool beginRenderPass(const GrVkRenderPass*,
                          const VkClearValue* colorClear,
                          GrVkRenderTarget*, GrSurfaceOrigin,
                          const SkIRect& bounds, bool forSecondaryCB);
@@ -177,10 +173,12 @@
 
     void destroyResources();
 
-    GrBackendTexture onCreateBackendTexture(int w, int h, const GrBackendFormat&,
-                                            GrMipMapped, GrRenderable,
-                                            const SkPixmap srcData[], int numMipLevels,
-                                            const SkColor4f* color, GrProtected) override;
+    GrBackendTexture onCreateBackendTexture(SkISize,
+                                            const GrBackendFormat&,
+                                            GrRenderable,
+                                            const BackendTextureData*,
+                                            int numMipLevels,
+                                            GrProtected) override;
     sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc&,
                                      const GrBackendFormat& format,
                                      GrRenderable,
@@ -231,7 +229,7 @@
     bool onCopySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect,
                        const SkIPoint& dstPoint) override;
 
-    void onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess access,
+    bool onFinishFlush(GrSurfaceProxy*[], int, SkSurface::BackendSurfaceAccess access,
                        const GrFlushInfo&, const GrPrepareForExternalIORequests&) override;
 
     // Ends and submits the current command buffer to the queue and then creates a new command
@@ -263,15 +261,19 @@
     void resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
                       const SkIPoint& dstPoint);
 
-    bool createVkImageForBackendSurface(VkFormat vkFormat, int w, int h, bool texturable,
-                                        bool renderable, GrMipMapped mipMapped,
-                                        const SkPixmap srcData[], int numMipLevels,
-                                        const SkColor4f* color, GrVkImageInfo* info,
-                                        GrProtected isProtected);
+    bool createVkImageForBackendSurface(VkFormat,
+                                        SkISize,
+                                        bool texturable,
+                                        bool renderable,
+                                        const BackendTextureData*,
+                                        int numMipLevels,
+                                        GrVkImageInfo*,
+                                        GrProtected);
 
     sk_sp<const GrVkInterface>                            fInterface;
     sk_sp<GrVkMemoryAllocator>                            fMemoryAllocator;
     sk_sp<GrVkCaps>                                       fVkCaps;
+    bool                                                  fDeviceIsLost = false;
 
     VkInstance                                            fInstance;
     VkPhysicalDevice                                      fPhysicalDevice;
diff --git a/src/gpu/vk/GrVkImage.cpp b/src/gpu/vk/GrVkImage.cpp
index 2b05d00..befb7d1 100644
--- a/src/gpu/vk/GrVkImage.cpp
+++ b/src/gpu/vk/GrVkImage.cpp
@@ -152,7 +152,7 @@
     this->updateImageLayout(newLayout);
 }
 
-bool GrVkImage::InitImageInfo(const GrVkGpu* gpu, const ImageDesc& imageDesc, GrVkImageInfo* info) {
+bool GrVkImage::InitImageInfo(GrVkGpu* gpu, const ImageDesc& imageDesc, GrVkImageInfo* info) {
     if (0 == imageDesc.fWidth || 0 == imageDesc.fHeight) {
         return false;
     }
@@ -197,8 +197,11 @@
         initialLayout                                // initialLayout
     };
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateImage(gpu->device(), &imageCreateInfo, nullptr,
-                                                        &image));
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, CreateImage(gpu->device(), &imageCreateInfo, nullptr, &image));
+    if (result != VK_SUCCESS) {
+        return false;
+    }
 
     if (!GrVkMemory::AllocAndBindImageMemory(gpu, image, isLinear, &alloc)) {
         VK_CALL(gpu, DestroyImage(gpu->device(), image, nullptr));
diff --git a/src/gpu/vk/GrVkImage.h b/src/gpu/vk/GrVkImage.h
index 8f44863..69ef5fe 100644
--- a/src/gpu/vk/GrVkImage.h
+++ b/src/gpu/vk/GrVkImage.h
@@ -137,7 +137,7 @@
                 , fIsProtected(GrProtected::kNo) {}
     };
 
-    static bool InitImageInfo(const GrVkGpu* gpu, const ImageDesc& imageDesc, GrVkImageInfo*);
+    static bool InitImageInfo(GrVkGpu* gpu, const ImageDesc& imageDesc, GrVkImageInfo*);
     // Destroys the internal VkImage and VkDeviceMemory in the GrVkImageInfo
     static void DestroyImageInfo(const GrVkGpu* gpu, GrVkImageInfo*);
 
diff --git a/src/gpu/vk/GrVkImageView.cpp b/src/gpu/vk/GrVkImageView.cpp
index ce7b1c5..123da92 100644
--- a/src/gpu/vk/GrVkImageView.cpp
+++ b/src/gpu/vk/GrVkImageView.cpp
@@ -52,8 +52,8 @@
         viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
     }
 
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateImageView(gpu->device(), &viewInfo,
-                                                                  nullptr, &imageView));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, CreateImageView(gpu->device(), &viewInfo, nullptr, &imageView));
     if (err) {
         return nullptr;
     }
diff --git a/src/gpu/vk/GrVkMemory.cpp b/src/gpu/vk/GrVkMemory.cpp
index ed14a64..8e76ed6 100644
--- a/src/gpu/vk/GrVkMemory.cpp
+++ b/src/gpu/vk/GrVkMemory.cpp
@@ -30,7 +30,7 @@
     SK_ABORT("Invalid GrVkBuffer::Type");
 }
 
-bool GrVkMemory::AllocAndBindBufferMemory(const GrVkGpu* gpu,
+bool GrVkMemory::AllocAndBindBufferMemory(GrVkGpu* gpu,
                                           VkBuffer buffer,
                                           GrVkBuffer::Type type,
                                           bool dynamic,
@@ -62,9 +62,9 @@
     allocator->getAllocInfo(memory, alloc);
 
     // Bind buffer
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), BindBufferMemory(gpu->device(), buffer,
-                                                                   alloc->fMemory,
-                                                                   alloc->fOffset));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, BindBufferMemory(gpu->device(), buffer, alloc->fMemory,
+                                                 alloc->fOffset));
     if (err) {
         FreeBufferMemory(gpu, type, *alloc);
         return false;
@@ -85,7 +85,7 @@
 
 const VkDeviceSize kMaxSmallImageSize = 256 * 1024;
 
-bool GrVkMemory::AllocAndBindImageMemory(const GrVkGpu* gpu,
+bool GrVkMemory::AllocAndBindImageMemory(GrVkGpu* gpu,
                                          VkImage image,
                                          bool linearTiling,
                                          GrVkAlloc* alloc) {
@@ -114,8 +114,9 @@
     allocator->getAllocInfo(memory, alloc);
 
     // Bind buffer
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), BindImageMemory(gpu->device(), image,
-                                                                  alloc->fMemory, alloc->fOffset));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, BindImageMemory(gpu->device(), image, alloc->fMemory,
+                                                alloc->fOffset));
     if (err) {
         FreeImageMemory(gpu, linearTiling, *alloc);
         return false;
@@ -134,7 +135,7 @@
     }
 }
 
-void* GrVkMemory::MapAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc) {
+void* GrVkMemory::MapAlloc(GrVkGpu* gpu, const GrVkAlloc& alloc) {
     SkASSERT(GrVkAlloc::kMappable_Flag & alloc.fFlags);
 #ifdef SK_DEBUG
     if (alloc.fFlags & GrVkAlloc::kNoncoherent_Flag) {
@@ -149,9 +150,9 @@
     }
 
     void* mapPtr;
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), MapMemory(gpu->device(), alloc.fMemory,
-                                                            alloc.fOffset,
-                                                            alloc.fSize, 0, &mapPtr));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, MapMemory(gpu->device(), alloc.fMemory, alloc.fOffset, alloc.fSize,
+                                          0, &mapPtr));
     if (err) {
         mapPtr = nullptr;
     }
diff --git a/src/gpu/vk/GrVkMemory.h b/src/gpu/vk/GrVkMemory.h
index 7244da0..db2c0e6 100644
--- a/src/gpu/vk/GrVkMemory.h
+++ b/src/gpu/vk/GrVkMemory.h
@@ -19,14 +19,14 @@
     * Allocates vulkan device memory and binds it to the gpu's device for the given object.
     * Returns true if allocation succeeded.
     */
-    bool AllocAndBindBufferMemory(const GrVkGpu* gpu,
+    bool AllocAndBindBufferMemory(GrVkGpu* gpu,
                                   VkBuffer buffer,
                                   GrVkBuffer::Type type,
                                   bool dynamic,
                                   GrVkAlloc* alloc);
     void FreeBufferMemory(const GrVkGpu* gpu, GrVkBuffer::Type type, const GrVkAlloc& alloc);
 
-    bool AllocAndBindImageMemory(const GrVkGpu* gpu,
+    bool AllocAndBindImageMemory(GrVkGpu* gpu,
                                  VkImage image,
                                  bool linearTiling,
                                  GrVkAlloc* alloc);
@@ -36,7 +36,7 @@
     // the hood, we may map more than the range of the GrVkAlloc (e.g. the entire VkDeviceMemory),
     // but the pointer returned will always be to the start of the GrVkAlloc. The caller should also
     // never assume more than the GrVkAlloc block has been mapped.
-    void* MapAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc);
+    void* MapAlloc(GrVkGpu* gpu, const GrVkAlloc& alloc);
     void UnmapAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc);
 
     // For the Flush and Invalidate calls, the offset should be relative to the GrVkAlloc. Thus this
diff --git a/src/gpu/vk/GrVkOpsRenderPass.cpp b/src/gpu/vk/GrVkOpsRenderPass.cpp
index c58c412..6a9ead7 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.cpp
+++ b/src/gpu/vk/GrVkOpsRenderPass.cpp
@@ -1,9 +1,9 @@
 /*
-* Copyright 2016 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 #include "src/gpu/vk/GrVkOpsRenderPass.h"
 
@@ -60,7 +60,7 @@
 
 GrVkOpsRenderPass::GrVkOpsRenderPass(GrVkGpu* gpu) : fGpu(gpu) {}
 
-void GrVkOpsRenderPass::init(const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
+bool GrVkOpsRenderPass::init(const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
                              const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
                              const SkPMColor4f& clearColor) {
 
@@ -101,17 +101,20 @@
                                   false);
     }
 
-    const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle();
+    const GrVkResourceProvider::CompatibleRPHandle& rpHandle =
+            vkRT->compatibleRenderPassHandle();
     if (rpHandle.isValid()) {
         fCurrentRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle,
                                                                      vkColorOps,
                                                                      vkStencilOps);
     } else {
-        fCurrentRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT,
+        fCurrentRenderPass = fGpu->resourceProvider().findRenderPass(vkRT,
                                                                      vkColorOps,
                                                                      vkStencilOps);
     }
-    SkASSERT(fCurrentRenderPass);
+    if (!fCurrentRenderPass) {
+        return false;
+    }
 
     VkClearValue vkClearColor;
     vkClearColor.color.float32[0] = clearColor[0];
@@ -121,14 +124,25 @@
 
     if (!fGpu->vkCaps().preferPrimaryOverSecondaryCommandBuffers()) {
         fCurrentSecondaryCommandBuffer = fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu);
-        fCurrentSecondaryCommandBuffer->begin(fGpu, vkRT->framebuffer(), fCurrentRenderPass);
+        if (!fCurrentSecondaryCommandBuffer) {
+            fCurrentRenderPass = nullptr;
+            return false;
+        }
+        fCurrentSecondaryCommandBuffer->begin(fGpu, vkRT->getFramebuffer(), fCurrentRenderPass);
     }
 
-    fGpu->beginRenderPass(fCurrentRenderPass, &vkClearColor, vkRT, fOrigin, fBounds,
-                          SkToBool(fCurrentSecondaryCommandBuffer));
+    if (!fGpu->beginRenderPass(fCurrentRenderPass, &vkClearColor, vkRT, fOrigin, fBounds,
+                               SkToBool(fCurrentSecondaryCommandBuffer))) {
+        if (fCurrentSecondaryCommandBuffer) {
+            fCurrentSecondaryCommandBuffer->end(fGpu);
+        }
+        fCurrentRenderPass = nullptr;
+        return false;
+    }
+    return true;
 }
 
-void GrVkOpsRenderPass::initWrapped() {
+bool GrVkOpsRenderPass::initWrapped() {
     GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget);
     SkASSERT(vkRT->wrapsSecondaryCommandBuffer());
     fCurrentRenderPass = vkRT->externalRenderPass();
@@ -137,7 +151,11 @@
 
     fCurrentSecondaryCommandBuffer.reset(
             GrVkSecondaryCommandBuffer::Create(vkRT->getExternalSecondaryCommandBuffer()));
+    if (!fCurrentSecondaryCommandBuffer) {
+        return false;
+    }
     fCurrentSecondaryCommandBuffer->begin(fGpu, nullptr, fCurrentRenderPass);
+    return true;
 }
 
 GrVkOpsRenderPass::~GrVkOpsRenderPass() {
@@ -163,9 +181,18 @@
     if (!fRenderTarget) {
         return;
     }
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
 
     // We don't want to actually submit the secondary command buffer if it is wrapped.
     if (this->wrapsSecondaryCommandBuffer()) {
+        // We pass the ownership of the GrVkSecondaryCommandBuffer to the special wrapped
+        // GrVkRenderTarget since it's lifetime matches the lifetime we need to keep the
+        // GrVkResources on the GrVkSecondaryCommandBuffer alive.
+        static_cast<GrVkRenderTarget*>(fRenderTarget)->addWrappedGrSecondaryCommandBuffer(
+                std::move(fCurrentSecondaryCommandBuffer));
         return;
     }
 
@@ -175,10 +202,10 @@
     fGpu->endRenderPass(fRenderTarget, fOrigin, fBounds);
 }
 
-void GrVkOpsRenderPass::set(GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
+bool GrVkOpsRenderPass::set(GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
                             const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
                             const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
-                            const SkTArray<GrTextureProxy*, true>& sampledProxies) {
+                            const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
     SkASSERT(!fRenderTarget);
     SkASSERT(fGpu == rt->getContext()->priv().getGpu());
 
@@ -190,6 +217,7 @@
 
     for (int i = 0; i < sampledProxies.count(); ++i) {
         if (sampledProxies[i]->isInstantiated()) {
+            SkASSERT(sampledProxies[i]->asTextureProxy());
             GrVkTexture* vkTex = static_cast<GrVkTexture*>(sampledProxies[i]->peekTexture());
             SkASSERT(vkTex);
             vkTex->setImageLayout(
@@ -202,16 +230,17 @@
     fBounds = bounds;
 
     if (this->wrapsSecondaryCommandBuffer()) {
-        this->initWrapped();
-        return;
+        return this->initWrapped();
     }
 
-    this->init(colorInfo, stencilInfo, colorInfo.fClearColor);
+    return this->init(colorInfo, stencilInfo, colorInfo.fClearColor);
 }
 
 void GrVkOpsRenderPass::reset() {
     if (fCurrentSecondaryCommandBuffer) {
-        fCurrentSecondaryCommandBuffer.release()->recycle(fGpu);
+        // The active GrVkCommandPool on the GrVkGpu should still be the same pool we got the
+        // secondary command buffer from since we haven't submitted any work yet.
+        fCurrentSecondaryCommandBuffer.release()->recycle(fGpu->cmdPool());
     }
     if (fCurrentRenderPass) {
         fCurrentRenderPass->unref(fGpu);
@@ -238,6 +267,11 @@
 }
 
 void GrVkOpsRenderPass::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
+
     SkASSERT(!clip.hasWindowRectangles());
 
     GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment();
@@ -289,6 +323,11 @@
 }
 
 void GrVkOpsRenderPass::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
+
     // parent class should never let us get here with no RT
     SkASSERT(!clip.hasWindowRectangles());
 
@@ -353,11 +392,13 @@
                                                                      vkColorOps,
                                                                      vkStencilOps);
     } else {
-        fCurrentRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT,
+        fCurrentRenderPass = fGpu->resourceProvider().findRenderPass(vkRT,
                                                                      vkColorOps,
                                                                      vkStencilOps);
     }
-    SkASSERT(fCurrentRenderPass);
+    if (!fCurrentRenderPass) {
+        return;
+    }
 
     VkClearValue vkClearColor;
     memset(&vkClearColor, 0, sizeof(VkClearValue));
@@ -365,16 +406,29 @@
     if (!fGpu->vkCaps().preferPrimaryOverSecondaryCommandBuffers() ||
         mustUseSecondaryCommandBuffer) {
         fCurrentSecondaryCommandBuffer = fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu);
-        fCurrentSecondaryCommandBuffer->begin(fGpu, vkRT->framebuffer(), fCurrentRenderPass);
+        if (!fCurrentSecondaryCommandBuffer) {
+            fCurrentRenderPass = nullptr;
+            return;
+        }
+        fCurrentSecondaryCommandBuffer->begin(fGpu, vkRT->getFramebuffer(), fCurrentRenderPass);
     }
 
     // We use the same fBounds as the whole GrVkOpsRenderPass since we have no way of tracking the
     // bounds in GrOpsTask for parts before and after inline uploads separately.
-    fGpu->beginRenderPass(fCurrentRenderPass, &vkClearColor, vkRT, fOrigin, fBounds,
-                          SkToBool(fCurrentSecondaryCommandBuffer));
+    if (!fGpu->beginRenderPass(fCurrentRenderPass, &vkClearColor, vkRT, fOrigin, fBounds,
+                               SkToBool(fCurrentSecondaryCommandBuffer))) {
+        if (fCurrentSecondaryCommandBuffer) {
+            fCurrentSecondaryCommandBuffer->end(fGpu);
+        }
+        fCurrentRenderPass = nullptr;
+    }
 }
 
 void GrVkOpsRenderPass::inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
     if (fCurrentSecondaryCommandBuffer) {
         fCurrentSecondaryCommandBuffer->end(fGpu);
         fGpu->submitSecondaryCommandBuffer(std::move(fCurrentSecondaryCommandBuffer));
@@ -428,7 +482,6 @@
 
 GrVkPipelineState* GrVkOpsRenderPass::prepareDrawState(
         const GrProgramInfo& programInfo,
-        GrPrimitiveType primitiveType,
         const SkIRect& renderPassScissorRect) {
     GrVkCommandBuffer* currentCB = this->currentCommandBuffer();
     SkASSERT(fCurrentRenderPass);
@@ -438,7 +491,6 @@
     GrVkPipelineState* pipelineState =
         fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget,
                                                                      programInfo,
-                                                                     primitiveType,
                                                                      compatibleRenderPass);
     if (!pipelineState) {
         return pipelineState;
@@ -447,17 +499,21 @@
     pipelineState->bindPipeline(fGpu, currentCB);
 
     // Both the 'programInfo' and this renderPass have an origin. Since they come from the
-    // same place (i.e., the target renderTargetProxy) that had best agree.
+    // same place (i.e., the target renderTargetProxy) they had best agree.
     SkASSERT(programInfo.origin() == fOrigin);
 
-    pipelineState->setAndBindUniforms(fGpu, fRenderTarget, programInfo, currentCB);
+    if (!pipelineState->setAndBindUniforms(fGpu, fRenderTarget, programInfo, currentCB)) {
+        return nullptr;
+    }
 
     // Check whether we need to bind textures between each GrMesh. If not we can bind them all now.
     if (!programInfo.hasDynamicPrimProcTextures()) {
         auto proxies = programInfo.hasFixedPrimProcTextures() ? programInfo.fixedPrimProcTextures()
                                                               : nullptr;
-        pipelineState->setAndBindTextures(fGpu, programInfo.primProc(), programInfo.pipeline(),
-                                          proxies, currentCB);
+        if (!pipelineState->setAndBindTextures(fGpu, programInfo.primProc(), programInfo.pipeline(),
+                                               proxies, currentCB)) {
+            return nullptr;
+        }
     }
 
     if (!programInfo.pipeline().isScissorEnabled()) {
@@ -493,6 +549,10 @@
 void GrVkOpsRenderPass::onDraw(const GrProgramInfo& programInfo,
                                const GrMesh meshes[], int meshCount,
                                const SkRect& bounds) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
 
     SkASSERT(meshCount); // guaranteed by GrOpsRenderPass::draw
 
@@ -515,19 +575,16 @@
         }
     }
 
-    GrFragmentProcessor::Iter iter(programInfo.pipeline());
-    while (const GrFragmentProcessor* fp = iter.next()) {
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            const GrFragmentProcessor::TextureSampler& sampler = fp->textureSampler(i);
-            check_sampled_texture(sampler.peekTexture(), fRenderTarget, fGpu);
-        }
+    GrFragmentProcessor::PipelineTextureSamplerRange textureSamplerRange(programInfo.pipeline());
+    for (auto [sampler, fp] : textureSamplerRange) {
+        check_sampled_texture(sampler.peekTexture(), fRenderTarget, fGpu);
     }
     if (GrTexture* dstTexture = programInfo.pipeline().peekDstTexture()) {
         check_sampled_texture(dstTexture, fRenderTarget, fGpu);
     }
 
     // Both the 'programInfo' and this renderPass have an origin. Since they come from the
-    // same place (i.e., the target renderTargetProxy) that had best agree.
+    // same place (i.e., the target renderTargetProxy) they had best agree.
     SkASSERT(programInfo.origin() == fOrigin);
 #endif
 
@@ -537,9 +594,7 @@
         scissorRect.roundOut(&renderPassScissorRect);
     }
 
-    GrPrimitiveType primitiveType = meshes[0].primitiveType();
-    GrVkPipelineState* pipelineState = this->prepareDrawState(programInfo, primitiveType,
-                                                              renderPassScissorRect);
+    GrVkPipelineState* pipelineState = this->prepareDrawState(programInfo, renderPassScissorRect);
     if (!pipelineState) {
         return;
     }
@@ -549,15 +604,8 @@
 
     for (int i = 0; i < meshCount; ++i) {
         const GrMesh& mesh = meshes[i];
-        if (mesh.primitiveType() != primitiveType) {
-            SkDEBUGCODE(pipelineState = nullptr);
-            primitiveType = mesh.primitiveType();
-            pipelineState = this->prepareDrawState(programInfo, primitiveType,
-                                                   renderPassScissorRect);
-            if (!pipelineState) {
-                return;
-            }
-        }
+
+        SkASSERT(programInfo.primitiveType() == mesh.primitiveType());
 
         if (hasDynamicScissors) {
             SkIRect combinedScissorRect;
@@ -571,8 +619,15 @@
         }
         if (hasDynamicTextures) {
             auto meshProxies = programInfo.dynamicPrimProcTextures(i);
-            pipelineState->setAndBindTextures(fGpu, programInfo.primProc(), programInfo.pipeline(),
-                                              meshProxies, this->currentCommandBuffer());
+            if (!pipelineState->setAndBindTextures(fGpu, programInfo.primProc(),
+                                                   programInfo.pipeline(), meshProxies,
+                                                   this->currentCommandBuffer())) {
+                if (fGpu->isDeviceLost()) {
+                    return;
+                } else {
+                    continue;
+                }
+            }
         }
         SkASSERT(pipelineState);
         mesh.sendToGpu(this);
@@ -623,6 +678,10 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrVkOpsRenderPass::executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable) {
+    if (!fCurrentRenderPass) {
+        SkASSERT(fGpu->isDeviceLost());
+        return;
+    }
     GrVkRenderTarget* target = static_cast<GrVkRenderTarget*>(fRenderTarget);
 
     GrVkImage* targetImage = target->msaaImage() ? target->msaaImage() : target;
@@ -634,6 +693,11 @@
     if (!fCurrentSecondaryCommandBuffer) {
         fGpu->endRenderPass(fRenderTarget, fOrigin, fBounds);
         this->addAdditionalRenderPass(true);
+        // We may have failed to start a new render pass
+        if (!fCurrentRenderPass) {
+            SkASSERT(fGpu->isDeviceLost());
+            return;
+        }
     }
     SkASSERT(fCurrentSecondaryCommandBuffer);
 
diff --git a/src/gpu/vk/GrVkOpsRenderPass.h b/src/gpu/vk/GrVkOpsRenderPass.h
index 65d6b5b..0e6a86e 100644
--- a/src/gpu/vk/GrVkOpsRenderPass.h
+++ b/src/gpu/vk/GrVkOpsRenderPass.h
@@ -38,10 +38,10 @@
 
     void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) override;
 
-    void set(GrRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds,
+    bool set(GrRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds,
              const GrOpsRenderPass::LoadAndStoreInfo&,
              const GrOpsRenderPass::StencilLoadAndStoreInfo&,
-             const SkTArray<GrTextureProxy*, true>& sampledProxies);
+             const SkTArray<GrSurfaceProxy*, true>& sampledProxies);
     void reset();
 
     void submit();
@@ -51,13 +51,13 @@
 #endif
 
 private:
-    void init(const GrOpsRenderPass::LoadAndStoreInfo&,
+    bool init(const GrOpsRenderPass::LoadAndStoreInfo&,
               const GrOpsRenderPass::StencilLoadAndStoreInfo&,
               const SkPMColor4f& clearColor);
 
     // Called instead of init when we are drawing to a render target that already wraps a secondary
     // command buffer.
-    void initWrapped();
+    bool initWrapped();
 
     bool wrapsSecondaryCommandBuffer() const;
 
@@ -70,8 +70,7 @@
                       const GrGpuBuffer* vertexBuffer,
                       const GrGpuBuffer* instanceBuffer);
 
-    GrVkPipelineState* prepareDrawState(const GrProgramInfo&, GrPrimitiveType,
-                                        const SkIRect& renderPassScissorRect);
+    GrVkPipelineState* prepareDrawState(const GrProgramInfo&, const SkIRect& renderPassScissorRect);
 
     void onDraw(const GrProgramInfo&, const GrMesh[], int meshCount,
                 const SkRect& bounds) override;
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 6868ff5..d3ab9ef 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -238,8 +238,11 @@
 }
 
 static void setup_depth_stencil_state(
-        const GrStencilSettings& stencilSettings, GrSurfaceOrigin origin,
+        const GrProgramInfo& programInfo,
         VkPipelineDepthStencilStateCreateInfo* stencilInfo) {
+    GrStencilSettings stencilSettings = programInfo.nonGLStencilSettings();
+    GrSurfaceOrigin origin = programInfo.origin();
+
     memset(stencilInfo, 0, sizeof(VkPipelineDepthStencilStateCreateInfo));
     stencilInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
     stencilInfo->pNext = nullptr;
@@ -252,11 +255,11 @@
     stencilInfo->stencilTestEnable = !stencilSettings.isDisabled();
     if (!stencilSettings.isDisabled()) {
         if (!stencilSettings.isTwoSided()) {
-            setup_stencil_op_state(&stencilInfo->front, stencilSettings.frontAndBack());
+            setup_stencil_op_state(&stencilInfo->front, stencilSettings.singleSidedFace());
             stencilInfo->back = stencilInfo->front;
         } else {
-            setup_stencil_op_state(&stencilInfo->front, stencilSettings.front(origin));
-            setup_stencil_op_state(&stencilInfo->back, stencilSettings.back(origin));
+            setup_stencil_op_state(&stencilInfo->front, stencilSettings.postOriginCCWFace(origin));
+            setup_stencil_op_state(&stencilInfo->back, stencilSettings.postOriginCWFace(origin));
         }
     }
     stencilInfo->minDepthBounds = 0.0f;
@@ -285,7 +288,7 @@
     multisampleInfo->sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
     multisampleInfo->pNext = nullptr;
     multisampleInfo->flags = 0;
-    SkAssertResult(GrSampleCountToVkSampleCount(programInfo.numSamples(),
+    SkAssertResult(GrSampleCountToVkSampleCount(programInfo.numRasterSamples(),
                                                 &multisampleInfo->rasterizationSamples));
     multisampleInfo->sampleShadingEnable = VK_FALSE;
     multisampleInfo->minSampleShading = 0.0f;
@@ -294,6 +297,42 @@
     multisampleInfo->alphaToOneEnable = VK_FALSE;
 }
 
+static void setup_all_sample_locations_at_pixel_center(
+        const GrProgramInfo& programInfo,
+        VkPipelineSampleLocationsStateCreateInfoEXT* sampleLocations) {
+    constexpr static VkSampleLocationEXT kCenteredSampleLocations[16] = {
+            {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f},
+            {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}, {.5f,.5f}};
+    memset(sampleLocations, 0, sizeof(VkPipelineSampleLocationsStateCreateInfoEXT));
+    sampleLocations->sType = VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT;
+    sampleLocations->pNext = nullptr;
+    sampleLocations->sampleLocationsEnable = VK_TRUE;
+    sampleLocations->sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT;
+    sampleLocations->sampleLocationsInfo.pNext = nullptr;
+    SkAssertResult(GrSampleCountToVkSampleCount(
+            programInfo.numRasterSamples(),
+            &sampleLocations->sampleLocationsInfo.sampleLocationsPerPixel));
+    sampleLocations->sampleLocationsInfo.sampleLocationGridSize.width = 1;
+    sampleLocations->sampleLocationsInfo.sampleLocationGridSize.height = 1;
+    SkASSERT(programInfo.numRasterSamples() < (int)SK_ARRAY_COUNT(kCenteredSampleLocations));
+    sampleLocations->sampleLocationsInfo.sampleLocationsCount = std::min(
+            programInfo.numRasterSamples(), (int)SK_ARRAY_COUNT(kCenteredSampleLocations));
+    sampleLocations->sampleLocationsInfo.pSampleLocations = kCenteredSampleLocations;
+}
+
+static void setup_coverage_modulation_state(
+        VkPipelineCoverageModulationStateCreateInfoNV* coverageModulationInfo) {
+    memset(coverageModulationInfo, 0, sizeof(VkPipelineCoverageModulationStateCreateInfoNV));
+    coverageModulationInfo->sType =
+            VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV;
+    coverageModulationInfo->pNext = nullptr;
+    coverageModulationInfo->flags = 0;
+    coverageModulationInfo->coverageModulationMode = VK_COVERAGE_MODULATION_MODE_RGBA_NV;
+    coverageModulationInfo->coverageModulationTableEnable = false;
+    coverageModulationInfo->coverageModulationTableCount = 0;
+    coverageModulationInfo->pCoverageModulationTable = nullptr;
+}
+
 static VkBlendFactor blend_coeff_to_vk_blend(GrBlendCoeff coeff) {
     static const VkBlendFactor gTable[] = {
         VK_BLEND_FACTOR_ZERO,                      // kZero_GrBlendCoeff
@@ -498,9 +537,8 @@
 GrVkPipeline* GrVkPipeline::Create(
         GrVkGpu* gpu,
         const GrProgramInfo& programInfo,
-        const GrStencilSettings& stencil,
         VkPipelineShaderStageCreateInfo* shaderStageInfo, int shaderStageCount,
-        GrPrimitiveType primitiveType, VkRenderPass compatibleRenderPass, VkPipelineLayout layout,
+        VkRenderPass compatibleRenderPass, VkPipelineLayout layout,
         VkPipelineCache cache) {
     VkPipelineVertexInputStateCreateInfo vertexInputInfo;
     SkSTArray<2, VkVertexInputBindingDescription, true> bindingDescs;
@@ -512,10 +550,10 @@
     setup_vertex_input_state(programInfo.primProc(), &vertexInputInfo, &bindingDescs, pAttribs);
 
     VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo;
-    setup_input_assembly_state(primitiveType, &inputAssemblyInfo);
+    setup_input_assembly_state(programInfo.primitiveType(), &inputAssemblyInfo);
 
     VkPipelineDepthStencilStateCreateInfo depthStencilInfo;
-    setup_depth_stencil_state(stencil, programInfo.origin(), &depthStencilInfo);
+    setup_depth_stencil_state(programInfo, &depthStencilInfo);
 
     VkPipelineViewportStateCreateInfo viewportInfo;
     setup_viewport_scissor_state(&viewportInfo);
@@ -523,6 +561,25 @@
     VkPipelineMultisampleStateCreateInfo multisampleInfo;
     setup_multisample_state(programInfo, gpu->caps(), &multisampleInfo);
 
+    VkPipelineSampleLocationsStateCreateInfoEXT sampleLocations;
+    if (gpu->caps()->multisampleDisableSupport()) {
+        if (programInfo.numRasterSamples() > 1 && !programInfo.pipeline().isHWAntialiasState()) {
+            setup_all_sample_locations_at_pixel_center(programInfo, &sampleLocations);
+            sampleLocations.pNext = multisampleInfo.pNext;
+            multisampleInfo.pNext = &sampleLocations;
+        }
+    }
+
+    VkPipelineCoverageModulationStateCreateInfoNV coverageModulationInfo;
+    if (gpu->caps()->mixedSamplesSupport()) {
+        if (programInfo.isMixedSampled()) {
+            SkASSERT(gpu->caps()->mixedSamplesSupport());
+            setup_coverage_modulation_state(&coverageModulationInfo);
+            coverageModulationInfo.pNext = multisampleInfo.pNext;
+            multisampleInfo.pNext = &coverageModulationInfo;
+        }
+    }
+
     // We will only have one color attachment per pipeline.
     VkPipelineColorBlendAttachmentState attachmentStates[1];
     VkPipelineColorBlendStateCreateInfo colorBlendInfo;
@@ -564,10 +621,9 @@
         // skia:8712
         __lsan::ScopedDisabler lsanDisabler;
 #endif
-        err = GR_VK_CALL(gpu->vkInterface(), CreateGraphicsPipelines(gpu->device(),
-                                                                     cache, 1,
-                                                                     &pipelineCreateInfo,
-                                                                     nullptr, &vkPipeline));
+        GR_VK_CALL_RESULT(gpu, err, CreateGraphicsPipelines(gpu->device(), cache, 1,
+                                                            &pipelineCreateInfo, nullptr,
+                                                            &vkPipeline));
     }
     if (err) {
         SkDebugf("Failed to create pipeline. Error: %d\n", err);
diff --git a/src/gpu/vk/GrVkPipeline.h b/src/gpu/vk/GrVkPipeline.h
index ce33a49..34b835b 100644
--- a/src/gpu/vk/GrVkPipeline.h
+++ b/src/gpu/vk/GrVkPipeline.h
@@ -26,10 +26,8 @@
 public:
     static GrVkPipeline* Create(GrVkGpu*,
                                 const GrProgramInfo&,
-                                const GrStencilSettings&,
                                 VkPipelineShaderStageCreateInfo* shaderStageInfo,
                                 int shaderStageCount,
-                                GrPrimitiveType primitiveType,
                                 VkRenderPass compatibleRenderPass,
                                 VkPipelineLayout layout,
                                 VkPipelineCache cache);
diff --git a/src/gpu/vk/GrVkPipelineState.cpp b/src/gpu/vk/GrVkPipelineState.cpp
index d511e71..7bcd342 100644
--- a/src/gpu/vk/GrVkPipelineState.cpp
+++ b/src/gpu/vk/GrVkPipelineState.cpp
@@ -1,9 +1,9 @@
 /*
-* Copyright 2016 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 #include "include/gpu/GrContext.h"
 #include "src/core/SkMipMap.h"
@@ -14,7 +14,6 @@
 #include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
 #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
 #include "src/gpu/glsl/GrGLSLXferProcessor.h"
-#include "src/gpu/vk/GrVkBufferView.h"
 #include "src/gpu/vk/GrVkCommandBuffer.h"
 #include "src/gpu/vk/GrVkDescriptorPool.h"
 #include "src/gpu/vk/GrVkDescriptorSet.h"
@@ -41,7 +40,6 @@
         int fragmentProcessorCnt)
         : fPipeline(pipeline)
         , fUniformDescriptorSet(nullptr)
-        , fSamplerDescriptorSet(nullptr)
         , fSamplerDSHandle(samplerDSHandle)
         , fBuiltinUniformHandles(builtinUniformHandles)
         , fGeometryProcessor(std::move(geometryProcessor))
@@ -49,10 +47,6 @@
         , fFragmentProcessors(std::move(fragmentProcessors))
         , fFragmentProcessorCnt(fragmentProcessorCnt)
         , fDataManager(uniforms, uniformSize) {
-    fDescriptorSets[0] = VK_NULL_HANDLE;
-    fDescriptorSets[1] = VK_NULL_HANDLE;
-    fDescriptorSets[2] = VK_NULL_HANDLE;
-
     fUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, uniformSize));
 
     fNumSamplers = samplers.count();
@@ -84,11 +78,6 @@
         fUniformDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
         fUniformDescriptorSet = nullptr;
     }
-
-    if (fSamplerDescriptorSet) {
-        fSamplerDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
-        fSamplerDescriptorSet = nullptr;
-    }
 }
 
 void GrVkPipelineState::abandonGPUResources() {
@@ -106,31 +95,22 @@
         fUniformDescriptorSet->unrefAndAbandon();
         fUniformDescriptorSet = nullptr;
     }
-
-    if (fSamplerDescriptorSet) {
-        fSamplerDescriptorSet->unrefAndAbandon();
-        fSamplerDescriptorSet = nullptr;
-    }
 }
 
-void GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
+bool GrVkPipelineState::setAndBindUniforms(GrVkGpu* gpu,
                                            const GrRenderTarget* renderTarget,
                                            const GrProgramInfo& programInfo,
                                            GrVkCommandBuffer* commandBuffer) {
     this->setRenderTargetState(renderTarget, programInfo.origin());
 
-    fGeometryProcessor->setData(fDataManager, programInfo.primProc(),
-                                GrFragmentProcessor::CoordTransformIter(programInfo.pipeline()));
-    GrFragmentProcessor::Iter iter(programInfo.pipeline());
+    GrFragmentProcessor::PipelineCoordTransformRange transformRange(programInfo.pipeline());
+    fGeometryProcessor->setData(fDataManager, programInfo.primProc(), transformRange);
+    GrFragmentProcessor::CIter fpIter(programInfo.pipeline());
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        glslFP->setData(fDataManager, *fp);
-        fp = iter.next();
-        glslFP = glslIter.next();
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        glslIter->setData(fDataManager, *fpIter);
     }
-    SkASSERT(!fp && !glslFP);
+    SkASSERT(!fpIter && !glslIter);
 
     {
         SkIPoint offset;
@@ -142,31 +122,31 @@
 
     // Get new descriptor set
     if (fUniformBuffer) {
-        int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
         if (fDataManager.uploadUniformBuffers(gpu, fUniformBuffer.get()) ||
             !fUniformDescriptorSet) {
             if (fUniformDescriptorSet) {
                 fUniformDescriptorSet->recycle(gpu);
             }
             fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
-            fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
+            if (!fUniformDescriptorSet) {
+                return false;
+            }
             this->writeUniformBuffers(gpu);
         }
-        commandBuffer->bindDescriptorSets(gpu, this, fPipeline->layout(), uniformDSIdx, 1,
-                                          &fDescriptorSets[uniformDSIdx], 0, nullptr);
-        if (fUniformDescriptorSet) {
-            commandBuffer->addRecycledResource(fUniformDescriptorSet);
-        }
-        if (fUniformBuffer) {
-            commandBuffer->addRecycledResource(fUniformBuffer->resource());
-        }
+        static const int kUniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
+        commandBuffer->bindDescriptorSets(gpu, this, fPipeline->layout(), kUniformDSIdx, 1,
+                                          fUniformDescriptorSet->descriptorSet(), 0, nullptr);
+        SkASSERT(fUniformDescriptorSet);
+        commandBuffer->addRecycledResource(fUniformDescriptorSet);
+        commandBuffer->addRecycledResource(fUniformBuffer->resource());
     }
+    return true;
 }
 
-void GrVkPipelineState::setAndBindTextures(GrVkGpu* gpu,
+bool GrVkPipelineState::setAndBindTextures(GrVkGpu* gpu,
                                            const GrPrimitiveProcessor& primProc,
                                            const GrPipeline& pipeline,
-                                           const GrTextureProxy* const primProcTextures[],
+                                           const GrSurfaceProxy* const primProcTextures[],
                                            GrVkCommandBuffer* commandBuffer) {
     SkASSERT(primProcTextures || !primProc.numTextureSamplers());
 
@@ -177,44 +157,54 @@
     SkAutoSTMalloc<8, SamplerBindings> samplerBindings(fNumSamplers);
     int currTextureBinding = 0;
 
-    fGeometryProcessor->setData(fDataManager, primProc,
-                                GrFragmentProcessor::CoordTransformIter(pipeline));
     for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
+        SkASSERT(primProcTextures[i]->asTextureProxy());
         const auto& sampler = primProc.textureSampler(i);
         auto texture = static_cast<GrVkTexture*>(primProcTextures[i]->peekTexture());
         samplerBindings[currTextureBinding++] = {sampler.samplerState(), texture};
     }
 
-    GrFragmentProcessor::Iter iter(pipeline);
+    GrFragmentProcessor::CIter fpIter(pipeline);
     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
-    const GrFragmentProcessor* fp = iter.next();
-    GrGLSLFragmentProcessor* glslFP = glslIter.next();
-    while (fp && glslFP) {
-        for (int i = 0; i < fp->numTextureSamplers(); ++i) {
-            const auto& sampler = fp->textureSampler(i);
+    for (; fpIter && glslIter; ++fpIter, ++glslIter) {
+        for (int i = 0; i < fpIter->numTextureSamplers(); ++i) {
+            const auto& sampler = fpIter->textureSampler(i);
             samplerBindings[currTextureBinding++] =
                     {sampler.samplerState(), static_cast<GrVkTexture*>(sampler.peekTexture())};
         }
-        fp = iter.next();
-        glslFP = glslIter.next();
     }
-    SkASSERT(!fp && !glslFP);
+    SkASSERT(!fpIter && !glslIter);
 
-    if (GrTextureProxy* dstTextureProxy = pipeline.dstTextureProxy()) {
+    if (GrTexture* dstTexture = pipeline.peekDstTexture()) {
         samplerBindings[currTextureBinding++] = {
-                GrSamplerState::ClampNearest(),
-                static_cast<GrVkTexture*>(dstTextureProxy->peekTexture())};
+                GrSamplerState::ClampNearest(), static_cast<GrVkTexture*>(dstTexture)};
     }
 
     // Get new descriptor set
     SkASSERT(fNumSamplers == currTextureBinding);
     if (fNumSamplers) {
-        if (fSamplerDescriptorSet) {
-            fSamplerDescriptorSet->recycle(gpu);
+        static const int kSamplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
+
+        if (fNumSamplers == 1) {
+            auto texture = samplerBindings[0].fTexture;
+            const auto& samplerState = samplerBindings[0].fState;
+            const GrVkDescriptorSet* descriptorSet = texture->cachedSingleDescSet(samplerState);
+            if (descriptorSet) {
+                commandBuffer->addResource(texture->textureView());
+                commandBuffer->addResource(texture->resource());
+                commandBuffer->addRecycledResource(descriptorSet);
+                commandBuffer->bindDescriptorSets(gpu, this, fPipeline->layout(), kSamplerDSIdx, 1,
+                                                  descriptorSet->descriptorSet(), 0, nullptr);
+                return true;
+            }
         }
-        fSamplerDescriptorSet = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
-        int samplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
-        fDescriptorSets[samplerDSIdx] = fSamplerDescriptorSet->descriptorSet();
+
+        const GrVkDescriptorSet* descriptorSet =
+                gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
+        if (!descriptorSet) {
+            return false;
+        }
+
         for (int i = 0; i < fNumSamplers; ++i) {
             const GrSamplerState& state = samplerBindings[i].fState;
             GrVkTexture* texture = samplerBindings[i].fTexture;
@@ -239,7 +229,7 @@
             memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
             writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
             writeInfo.pNext = nullptr;
-            writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
+            writeInfo.dstSet = *descriptorSet->descriptorSet();
             writeInfo.dstBinding = i;
             writeInfo.dstArrayElement = 0;
             writeInfo.descriptorCount = 1;
@@ -257,11 +247,18 @@
             commandBuffer->addResource(samplerBindings[i].fTexture->textureView());
             commandBuffer->addResource(samplerBindings[i].fTexture->resource());
         }
+        if (fNumSamplers == 1) {
+            const GrSamplerState& state = samplerBindings[0].fState;
+            GrVkTexture* texture = samplerBindings[0].fTexture;
+            texture->addDescriptorSetToCache(descriptorSet, state);
+        }
 
-        commandBuffer->bindDescriptorSets(gpu, this, fPipeline->layout(), samplerDSIdx, 1,
-                                          &fDescriptorSets[samplerDSIdx], 0, nullptr);
-        commandBuffer->addRecycledResource(fSamplerDescriptorSet);
+        commandBuffer->bindDescriptorSets(gpu, this, fPipeline->layout(), kSamplerDSIdx, 1,
+                                          descriptorSet->descriptorSet(), 0, nullptr);
+        commandBuffer->addRecycledResource(descriptorSet);
+        descriptorSet->recycle(gpu);
     }
+    return true;
 }
 
 void set_uniform_descriptor_writes(VkWriteDescriptorSet* descriptorWrite,
@@ -294,10 +291,11 @@
     uint32_t writeCount = 0;
 
     if (fUniformBuffer.get()) {
+        SkASSERT(fUniformDescriptorSet);
         set_uniform_descriptor_writes(&descriptorWrites[writeCount],
                                       &bufferInfos[writeCount],
                                       fUniformBuffer.get(),
-                                      fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet]);
+                                      *fUniformDescriptorSet->descriptorSet());
         ++writeCount;
     }
 
diff --git a/src/gpu/vk/GrVkPipelineState.h b/src/gpu/vk/GrVkPipelineState.h
index 331bb01..8d973df 100644
--- a/src/gpu/vk/GrVkPipelineState.h
+++ b/src/gpu/vk/GrVkPipelineState.h
@@ -16,7 +16,6 @@
 
 class GrPipeline;
 class GrStencilSettings;
-class GrVkBufferView;
 class GrVkCommandBuffer;
 class GrVkDescriptorPool;
 class GrVkDescriptorSet;
@@ -53,14 +52,14 @@
 
     ~GrVkPipelineState();
 
-    void setAndBindUniforms(GrVkGpu*, const GrRenderTarget*, const GrProgramInfo&,
+    bool setAndBindUniforms(GrVkGpu*, const GrRenderTarget*, const GrProgramInfo&,
                             GrVkCommandBuffer*);
     /**
      * This must be called after setAndBindUniforms() since that function invalidates texture
      * bindings.
      */
-    void setAndBindTextures(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
-                            const GrTextureProxy* const primitiveProcessorTextures[],
+    bool setAndBindTextures(GrVkGpu*, const GrPrimitiveProcessor&, const GrPipeline&,
+                            const GrSurfaceProxy* const primitiveProcessorTextures[],
                             GrVkCommandBuffer*);
 
     void bindPipeline(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer);
@@ -116,19 +115,11 @@
     // GrVkResources
     GrVkPipeline* fPipeline;
 
-    // The DescriptorSets need to survive until the gpu has finished all draws that use them.
-    // However, they will only be freed by the descriptor pool. Thus by simply keeping the
-    // descriptor pool alive through the draw, the descritor sets will also stay alive. Thus we do
-    // not need a GrVkResource versions of VkDescriptorSet. We hold on to these in the
-    // GrVkPipelineState since we update the descriptor sets and bind them at separate times;
-    VkDescriptorSet fDescriptorSets[3];
-
     const GrVkDescriptorSet* fUniformDescriptorSet;
-    const GrVkDescriptorSet* fSamplerDescriptorSet;
 
     const GrVkDescriptorSetManager::Handle fSamplerDSHandle;
 
-    SkSTArray<4, const GrVkSampler*>   fImmutableSamplers;
+    SkSTArray<4, const GrVkSampler*> fImmutableSamplers;
 
     std::unique_ptr<GrVkUniformBuffer> fUniformBuffer;
 
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index dbe16f6..41dca29 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -24,9 +24,7 @@
         GrVkGpu* gpu,
         GrRenderTarget* renderTarget,
         const GrProgramInfo& programInfo,
-        const GrStencilSettings& stencil,
-        GrPrimitiveType primitiveType,
-        Desc* desc,
+        GrProgramDesc* desc,
         VkRenderPass compatibleRenderPass) {
     // ensure that we use "." as a decimal separator when creating SkSL code
     GrAutoLocaleSetter als("C");
@@ -39,7 +37,7 @@
         return nullptr;
     }
 
-    return builder.finalize(stencil, primitiveType, compatibleRenderPass, desc);
+    return builder.finalize(compatibleRenderPass, desc);
 }
 
 GrVkPipelineStateBuilder::GrVkPipelineStateBuilder(GrVkGpu* gpu,
@@ -68,7 +66,7 @@
                                                     VkShaderModule* shaderModule,
                                                     VkPipelineShaderStageCreateInfo* stageInfo,
                                                     const SkSL::Program::Settings& settings,
-                                                    Desc* desc,
+                                                    GrProgramDesc* desc,
                                                     SkSL::String* outSPIRV,
                                                     SkSL::Program::Inputs* outInputs) {
     if (!GrCompileVkShaderModule(fGpu, sksl, stage, shaderModule,
@@ -137,21 +135,22 @@
 void GrVkPipelineStateBuilder::storeShadersInCache(const SkSL::String shaders[],
                                                    const SkSL::Program::Inputs inputs[],
                                                    bool isSkSL) {
-    const Desc* desc = static_cast<const Desc*>(this->desc());
     // Here we shear off the Vk-specific portion of the Desc in order to create the
     // persistent key. This is bc Vk only caches the SPIRV code, not the fully compiled
     // program, and that only depends on the base GrProgramDesc data.
-    sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength());
+    // The +4 is to include the kShader_PersistentCacheKeyType code the Vulkan backend adds
+    // to the key right after the base key.
+    sk_sp<SkData> key = SkData::MakeWithoutCopy(this->desc()->asKey(),
+                                                this->desc()->initialKeyLength()+4);
+
     sk_sp<SkData> data = GrPersistentCacheUtils::PackCachedShaders(isSkSL ? kSKSL_Tag : kSPIRV_Tag,
                                                                    shaders,
                                                                    inputs, kGrShaderTypeCount);
     this->gpu()->getContext()->priv().getPersistentCache()->store(*key, *data);
 }
 
-GrVkPipelineState* GrVkPipelineStateBuilder::finalize(const GrStencilSettings& stencil,
-                                                      GrPrimitiveType primitiveType,
-                                                      VkRenderPass compatibleRenderPass,
-                                                      Desc* desc) {
+GrVkPipelineState* GrVkPipelineStateBuilder::finalize(VkRenderPass compatibleRenderPass,
+                                                      GrProgramDesc* desc) {
     VkDescriptorSetLayout dsLayout[2];
     VkPipelineLayout pipelineLayout;
     VkShaderModule shaderModules[kGrShaderTypeCount] = { VK_NULL_HANDLE,
@@ -179,10 +178,12 @@
     layoutCreateInfo.pushConstantRangeCount = 0;
     layoutCreateInfo.pPushConstantRanges = nullptr;
 
-    GR_VK_CALL_ERRCHECK(fGpu->vkInterface(), CreatePipelineLayout(fGpu->device(),
-                                                                  &layoutCreateInfo,
-                                                                  nullptr,
-                                                                  &pipelineLayout));
+    VkResult result;
+    GR_VK_CALL_RESULT(fGpu, result, CreatePipelineLayout(fGpu->device(), &layoutCreateInfo, nullptr,
+                                                         &pipelineLayout));
+    if (result != VK_SUCCESS) {
+        return nullptr;
+    }
 
     // We need to enable the following extensions so that the compiler can correctly make spir-v
     // from our glsl shaders.
@@ -211,7 +212,9 @@
         // Here we shear off the Vk-specific portion of the Desc in order to create the
         // persistent key. This is bc Vk only caches the SPIRV code, not the fully compiled
         // program, and that only depends on the base GrProgramDesc data.
-        sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength());
+        // The +4 is to include the kShader_PersistentCacheKeyType code the Vulkan backend adds
+        // to the key right after the base key.
+        sk_sp<SkData> key = SkData::MakeWithoutCopy(desc->asKey(), desc->initialKeyLength()+4);
         cached = persistentCache->load(*key);
         if (cached) {
             reader.setMemory(cached->data(), cached->size());
@@ -295,8 +298,10 @@
             this->storeShadersInCache(shaders, inputs, isSkSL);
         }
     }
-        GrVkPipeline* pipeline = resourceProvider.createPipeline(fProgramInfo, stencil,
-            shaderStageInfo, numShaderStages, primitiveType, compatibleRenderPass, pipelineLayout);
+
+    GrVkPipeline* pipeline = resourceProvider.createPipeline(fProgramInfo, shaderStageInfo,
+                                                             numShaderStages, compatibleRenderPass,
+                                                             pipelineLayout);
     for (int i = 0; i < kGrShaderTypeCount; ++i) {
         // This if check should not be needed since calling destroy on a VK_NULL_HANDLE is allowed.
         // However this is causing a crash in certain drivers (e.g. NVidia).
@@ -324,34 +329,3 @@
                                  std::move(fFragmentProcessors),
                                  fFragmentProcessorCnt);
 }
-
-//////////////////////////////////////////////////////////////////////////////
-
-bool GrVkPipelineStateBuilder::Desc::Build(Desc* desc,
-                                           GrRenderTarget* renderTarget,
-                                           const GrProgramInfo& programInfo,
-                                           const GrStencilSettings& stencil,
-                                           GrPrimitiveType primitiveType,
-                                           GrVkGpu* gpu) {
-    if (!GrProgramDesc::Build(desc, renderTarget, programInfo, primitiveType, gpu)) {
-        return false;
-    }
-
-    GrProcessorKeyBuilder b(&desc->key());
-
-    b.add32(GrVkGpu::kShader_PersistentCacheKeyType);
-    int keyLength = desc->key().count();
-    SkASSERT(0 == (keyLength % 4));
-    desc->fShaderKeyLength = SkToU32(keyLength);
-
-    GrVkRenderTarget* vkRT = (GrVkRenderTarget*)renderTarget;
-    vkRT->simpleRenderPass()->genKey(&b);
-
-    stencil.genKey(&b);
-
-    b.add32(programInfo.pipeline().getBlendInfoKey());
-
-    b.add32((uint32_t)primitiveType);
-
-    return true;
-}
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.h b/src/gpu/vk/GrVkPipelineStateBuilder.h
index 0ba7fc5..b09b939 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.h
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.h
@@ -10,63 +10,31 @@
 
 #include "include/gpu/vk/GrVkTypes.h"
 #include "src/gpu/GrPipeline.h"
-#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
 #include "src/gpu/vk/GrVkPipelineState.h"
 #include "src/gpu/vk/GrVkUniformHandler.h"
 #include "src/gpu/vk/GrVkVaryingHandler.h"
 #include "src/sksl/SkSLCompiler.h"
 
+class GrProgramDesc;
 class GrVkGpu;
 class GrVkRenderPass;
 class SkReader32;
 
 class GrVkPipelineStateBuilder : public GrGLSLProgramBuilder {
 public:
-    /**
-     * For Vulkan we want to cache the entire VkPipeline for reuse of draws. The Desc here holds all
-     * the information needed to differentiate one pipeline from another.
-     *
-     * The GrProgramDesc contains all the information need to create the actual shaders for the
-     * pipeline.
-     *
-     * For Vulkan we need to add to the GrProgramDesc to include the rest of the state on the
-     * pipline. This includes stencil settings, blending information, render pass format, draw face
-     * information, and primitive type. Note that some state is set dynamically on the pipeline for
-     * each draw  and thus is not included in this descriptor. This includes the viewport, scissor,
-     * and blend constant.
-     */
-    class Desc : public GrProgramDesc {
-    public:
-        static bool Build(Desc*,
-                          GrRenderTarget*,
-                          const GrProgramInfo&,
-                          const GrStencilSettings&,
-                          GrPrimitiveType primitiveType,
-                          GrVkGpu* gpu);
-
-        size_t shaderKeyLength() const { return fShaderKeyLength; }
-
-    private:
-        size_t fShaderKeyLength;
-
-        typedef GrProgramDesc INHERITED;
-    };
-
     /** Generates a pipeline state.
-    *
-    * The GrVkPipelineState implements what is specified in the GrPipeline and GrPrimitiveProcessor
-    * as input. After successful generation, the builder result objects are available to be used.
-    * This function may modify the program key by setting the surface origin key to 0 (unspecified)
-    * if it turns out the program does not care about the surface origin.
-    * @return true if generation was successful.
-    */
+     *
+     * The GrVkPipelineState implements what is specified in the GrPipeline and GrPrimitiveProcessor
+     * as input. After successful generation, the builder result objects are available to be used.
+     * This function may modify the program key by setting the surface origin key to 0 (unspecified)
+     * if it turns out the program does not care about the surface origin.
+     * @return true if generation was successful.
+     */
     static GrVkPipelineState* CreatePipelineState(GrVkGpu*,
                                                   GrRenderTarget*,
                                                   const GrProgramInfo&,
-                                                  const GrStencilSettings&,
-                                                  GrPrimitiveType,
-                                                  Desc*,
+                                                  GrProgramDesc*,
                                                   VkRenderPass compatibleRenderPass);
 
     const GrCaps* caps() const override;
@@ -79,10 +47,7 @@
 private:
     GrVkPipelineStateBuilder(GrVkGpu*, GrRenderTarget*, const GrProgramInfo&, GrProgramDesc*);
 
-    GrVkPipelineState* finalize(const GrStencilSettings&,
-                                GrPrimitiveType primitiveType,
-                                VkRenderPass compatibleRenderPass,
-                                Desc*);
+    GrVkPipelineState* finalize(VkRenderPass compatibleRenderPass, GrProgramDesc*);
 
     // returns number of shader stages
     int loadShadersFromCache(SkReader32* cached, VkShaderModule outShaderModules[],
@@ -96,7 +61,7 @@
                               VkShaderModule* shaderModule,
                               VkPipelineShaderStageCreateInfo* stageInfo,
                               const SkSL::Program::Settings& settings,
-                              Desc* desc,
+                              GrProgramDesc* desc,
                               SkSL::String* outSPIRV,
                               SkSL::Program::Inputs* outInputs);
 
diff --git a/src/gpu/vk/GrVkPipelineStateCache.cpp b/src/gpu/vk/GrVkPipelineStateCache.cpp
index da6a69f..6081643 100644
--- a/src/gpu/vk/GrVkPipelineStateCache.cpp
+++ b/src/gpu/vk/GrVkPipelineStateCache.cpp
@@ -79,25 +79,20 @@
 GrVkPipelineState* GrVkResourceProvider::PipelineStateCache::refPipelineState(
         GrRenderTarget* renderTarget,
         const GrProgramInfo& programInfo,
-        GrPrimitiveType primitiveType,
         VkRenderPass compatibleRenderPass) {
 #ifdef GR_PIPELINE_STATE_CACHE_STATS
     ++fTotalRequests;
 #endif
-    GrStencilSettings stencil;
-    if (programInfo.pipeline().isStencilEnabled()) {
-        // TODO: attach stencil and create settings during render target flush.
-        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
-        stencil.reset(*programInfo.pipeline().getUserStencil(),
-                      programInfo.pipeline().hasStencilClip(),
-                      renderTarget->renderTargetPriv().numStencilBits());
-    }
 
-    // TODO: can this be unified between GL, Vk and Mtl?
-    // Get GrVkProgramDesc
-    GrVkPipelineStateBuilder::Desc desc;
-    if (!GrVkPipelineStateBuilder::Desc::Build(&desc, renderTarget, programInfo, stencil,
-                                               primitiveType, fGpu)) {
+#ifdef SK_DEBUG
+    if (programInfo.pipeline().isStencilEnabled()) {
+        SkASSERT(renderTarget->renderTargetPriv().getStencilAttachment());
+        SkASSERT(renderTarget->renderTargetPriv().numStencilBits() == 8);
+    }
+#endif
+
+    GrProgramDesc desc = fGpu->caps()->makeDesc(renderTarget, programInfo);
+    if (!desc.isValid()) {
         GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
         return nullptr;
     }
@@ -108,8 +103,7 @@
         ++fCacheMisses;
 #endif
         GrVkPipelineState* pipelineState(GrVkPipelineStateBuilder::CreatePipelineState(
-                fGpu, renderTarget, programInfo,
-                stencil, primitiveType, &desc, compatibleRenderPass));
+                fGpu, renderTarget, programInfo, &desc, compatibleRenderPass));
         if (!pipelineState) {
             return nullptr;
         }
diff --git a/src/gpu/vk/GrVkRenderPass.cpp b/src/gpu/vk/GrVkRenderPass.cpp
index d73ae61..818b3b8 100644
--- a/src/gpu/vk/GrVkRenderPass.cpp
+++ b/src/gpu/vk/GrVkRenderPass.cpp
@@ -42,17 +42,34 @@
     attachment->finalLayout = layout;
 }
 
-void GrVkRenderPass::initSimple(const GrVkGpu* gpu, const GrVkRenderTarget& target) {
+GrVkRenderPass* GrVkRenderPass::CreateSimple(GrVkGpu* gpu, const GrVkRenderTarget& target) {
     static const GrVkRenderPass::LoadStoreOps kBasicLoadStoreOps(VK_ATTACHMENT_LOAD_OP_LOAD,
                                                                  VK_ATTACHMENT_STORE_OP_STORE);
 
-    this->init(gpu, target, kBasicLoadStoreOps, kBasicLoadStoreOps);
+    AttachmentFlags attachmentFlags;
+    AttachmentsDescriptor attachmentsDescriptor;
+    // Get attachment information from render target. This includes which attachments the render
+    // target has (color, stencil) and the attachments format and sample count.
+    target.getAttachmentsDescriptor(&attachmentsDescriptor, &attachmentFlags);
+    return Create(gpu, attachmentFlags, attachmentsDescriptor, kBasicLoadStoreOps,
+                  kBasicLoadStoreOps);
 }
 
-void GrVkRenderPass::init(const GrVkGpu* gpu,
-                          const LoadStoreOps& colorOp,
-                          const LoadStoreOps& stencilOp) {
-    uint32_t numAttachments = fAttachmentsDescriptor.fAttachmentCount;
+GrVkRenderPass* GrVkRenderPass::Create(GrVkGpu* gpu,
+                            const GrVkRenderPass& compatibleRenderPass,
+                            const LoadStoreOps& colorOp,
+                            const LoadStoreOps& stencilOp) {
+    AttachmentFlags attachmentFlags = compatibleRenderPass.fAttachmentFlags;
+    AttachmentsDescriptor attachmentsDescriptor = compatibleRenderPass.fAttachmentsDescriptor;
+    return Create(gpu, attachmentFlags, attachmentsDescriptor, colorOp, stencilOp);
+}
+
+GrVkRenderPass* GrVkRenderPass::Create(GrVkGpu* gpu,
+                                       AttachmentFlags attachmentFlags,
+                                       AttachmentsDescriptor& attachmentsDescriptor,
+                                       const LoadStoreOps& colorOp,
+                                       const LoadStoreOps& stencilOp) {
+    uint32_t numAttachments = attachmentsDescriptor.fAttachmentCount;
     // Attachment descriptions to be set on the render pass
     SkTArray<VkAttachmentDescription> attachments(numAttachments);
     attachments.reset(numAttachments);
@@ -74,11 +91,13 @@
     subpassDesc.pInputAttachments = nullptr;
     subpassDesc.pResolveAttachments = nullptr;
 
-    if (fAttachmentFlags & kColor_AttachmentFlag) {
+    uint32_t clearValueCount = 0;
+
+    if (attachmentFlags & kColor_AttachmentFlag) {
         // set up color attachment
-        fAttachmentsDescriptor.fColor.fLoadStoreOps = colorOp;
+        attachmentsDescriptor.fColor.fLoadStoreOps = colorOp;
         setup_vk_attachment_description(&attachments[currentAttachment],
-                                        fAttachmentsDescriptor.fColor,
+                                        attachmentsDescriptor.fColor,
                                         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
         // setup subpass use of attachment
         colorRef.attachment = currentAttachment++;
@@ -86,7 +105,7 @@
         subpassDesc.colorAttachmentCount = 1;
 
         if (VK_ATTACHMENT_LOAD_OP_CLEAR == colorOp.fLoadOp) {
-            fClearValueCount = colorRef.attachment + 1;
+            clearValueCount = colorRef.attachment + 1;
         }
     } else {
         // I don't think there should ever be a time where we don't have a color attachment
@@ -97,17 +116,17 @@
     }
     subpassDesc.pColorAttachments = &colorRef;
 
-    if (fAttachmentFlags & kStencil_AttachmentFlag) {
+    if (attachmentFlags & kStencil_AttachmentFlag) {
         // set up stencil attachment
-        fAttachmentsDescriptor.fStencil.fLoadStoreOps = stencilOp;
+        attachmentsDescriptor.fStencil.fLoadStoreOps = stencilOp;
         setup_vk_attachment_description(&attachments[currentAttachment],
-                                        fAttachmentsDescriptor.fStencil,
+                                        attachmentsDescriptor.fStencil,
                                         VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
         // setup subpass use of attachment
         stencilRef.attachment = currentAttachment++;
         stencilRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
         if (VK_ATTACHMENT_LOAD_OP_CLEAR == stencilOp.fLoadOp) {
-            fClearValueCount = SkTMax(fClearValueCount, stencilRef.attachment + 1);
+            clearValueCount = SkTMax(clearValueCount, stencilRef.attachment + 1);
         }
     } else {
         stencilRef.attachment = VK_ATTACHMENT_UNUSED;
@@ -133,34 +152,34 @@
     createInfo.dependencyCount = 0;
     createInfo.pDependencies = nullptr;
 
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateRenderPass(gpu->device(),
-                                                             &createInfo,
-                                                             nullptr,
-                                                             &fRenderPass));
+    VkResult result;
+    VkRenderPass renderPass;
+    GR_VK_CALL_RESULT(gpu, result, CreateRenderPass(gpu->device(),
+                                                    &createInfo,
+                                                    nullptr,
+                                                    &renderPass));
+    if (result != VK_SUCCESS) {
+        return nullptr;
+    }
 
+    VkExtent2D granularity;
     // Get granularity for this render pass
     GR_VK_CALL(gpu->vkInterface(), GetRenderAreaGranularity(gpu->device(),
-                                                            fRenderPass,
-                                                            &fGranularity));
+                                                            renderPass,
+                                                            &granularity));
+
+    return new GrVkRenderPass(renderPass, attachmentFlags, attachmentsDescriptor, granularity,
+                              clearValueCount);
 }
 
-void GrVkRenderPass::init(const GrVkGpu* gpu,
-                          const GrVkRenderPass& compatibleRenderPass,
-                          const LoadStoreOps& colorOp,
-                          const LoadStoreOps& stencilOp) {
-    fAttachmentFlags = compatibleRenderPass.fAttachmentFlags;
-    fAttachmentsDescriptor = compatibleRenderPass.fAttachmentsDescriptor;
-    this->init(gpu, colorOp, stencilOp);
-}
-
-void GrVkRenderPass::init(const GrVkGpu* gpu,
-                          const GrVkRenderTarget& target,
-                          const LoadStoreOps& colorOp,
-                          const LoadStoreOps& stencilOp) {
-    // Get attachment information from render target. This includes which attachments the render
-    // target has (color, stencil) and the attachments format and sample count.
-    target.getAttachmentsDescriptor(&fAttachmentsDescriptor, &fAttachmentFlags);
-    this->init(gpu, colorOp, stencilOp);
+GrVkRenderPass::GrVkRenderPass(VkRenderPass renderPass, AttachmentFlags flags,
+                               const AttachmentsDescriptor& descriptor,
+                               const VkExtent2D& granularity, uint32_t clearValueCount)
+        : fRenderPass(renderPass)
+        , fAttachmentFlags(flags)
+        , fAttachmentsDescriptor(descriptor)
+        , fGranularity(granularity)
+        , fClearValueCount(clearValueCount) {
 }
 
 void GrVkRenderPass::freeGPUData(GrVkGpu* gpu) const {
diff --git a/src/gpu/vk/GrVkRenderPass.h b/src/gpu/vk/GrVkRenderPass.h
index 725b196..1474c7b 100644
--- a/src/gpu/vk/GrVkRenderPass.h
+++ b/src/gpu/vk/GrVkRenderPass.h
@@ -18,17 +18,6 @@
 
 class GrVkRenderPass : public GrVkResource {
 public:
-    GrVkRenderPass() : INHERITED(), fRenderPass(VK_NULL_HANDLE), fClearValueCount(0) {}
-
-    // Used when importing an external render pass. In this case we have to explicitly be told the
-    // color attachment index
-    explicit GrVkRenderPass(VkRenderPass renderPass, uint32_t colorAttachmentIndex)
-            : INHERITED()
-            , fRenderPass(renderPass)
-            , fAttachmentFlags(kExternal_AttachmentFlag)
-            , fClearValueCount(0)
-            , fColorAttachmentIndex(colorAttachmentIndex) {}
-
     struct LoadStoreOps {
         VkAttachmentLoadOp  fLoadOp;
         VkAttachmentStoreOp fStoreOp;
@@ -46,16 +35,20 @@
         }
     };
 
-    void initSimple(const GrVkGpu* gpu, const GrVkRenderTarget& target);
-    void init(const GrVkGpu* gpu,
-              const GrVkRenderTarget& target,
-              const LoadStoreOps& colorOp,
-              const LoadStoreOps& stencilOp);
+    static GrVkRenderPass* CreateSimple(GrVkGpu* gpu, const GrVkRenderTarget& target);
+    static GrVkRenderPass* Create(GrVkGpu* gpu,
+                                  const GrVkRenderPass& compatibleRenderPass,
+                                  const LoadStoreOps& colorOp,
+                                  const LoadStoreOps& stencilOp);
 
-    void init(const GrVkGpu* gpu,
-              const GrVkRenderPass& compatibleRenderPass,
-              const LoadStoreOps& colorOp,
-              const LoadStoreOps& stencilOp);
+    // Used when importing an external render pass. In this case we have to explicitly be told the
+    // color attachment index
+    explicit GrVkRenderPass(VkRenderPass renderPass, uint32_t colorAttachmentIndex)
+            : INHERITED()
+            , fRenderPass(renderPass)
+            , fAttachmentFlags(kExternal_AttachmentFlag)
+            , fClearValueCount(0)
+            , fColorAttachmentIndex(colorAttachmentIndex) {}
 
     struct AttachmentsDescriptor {
         struct AttachmentDesc {
@@ -132,11 +125,14 @@
 #endif
 
 private:
-    GrVkRenderPass(const GrVkRenderPass&);
+    GrVkRenderPass(VkRenderPass, AttachmentFlags, const AttachmentsDescriptor&,
+                   const VkExtent2D& granularity, uint32_t clearValueCount);
 
-    void init(const GrVkGpu* gpu,
-              const LoadStoreOps& colorOps,
-              const LoadStoreOps& stencilOps);
+    static GrVkRenderPass* Create(GrVkGpu* gpu,
+                                  AttachmentFlags,
+                                  AttachmentsDescriptor&,
+                                  const LoadStoreOps& colorOps,
+                                  const LoadStoreOps& stencilOps);
 
     bool isCompatible(const AttachmentsDescriptor&, const AttachmentFlags&) const;
 
diff --git a/src/gpu/vk/GrVkRenderTarget.cpp b/src/gpu/vk/GrVkRenderTarget.cpp
index d7d53fb..1494bca 100644
--- a/src/gpu/vk/GrVkRenderTarget.cpp
+++ b/src/gpu/vk/GrVkRenderTarget.cpp
@@ -39,11 +39,10 @@
         , fMSAAImage(new GrVkImage(msaaInfo, std::move(msaaLayout),
                                    GrBackendObjectOwnership::kOwned))
         , fResolveAttachmentView(resolveAttachmentView)
-        , fFramebuffer(nullptr)
+        , fCachedFramebuffer(nullptr)
         , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(info.fProtected == msaaInfo.fProtected);
     SkASSERT(sampleCnt > 1);
-    this->createFramebuffer(gpu);
     this->registerWithCacheWrapped(GrWrapCacheable::kNo);
 }
 
@@ -67,11 +66,10 @@
         , fMSAAImage(
                   new GrVkImage(msaaInfo, std::move(msaaLayout), GrBackendObjectOwnership::kOwned))
         , fResolveAttachmentView(resolveAttachmentView)
-        , fFramebuffer(nullptr)
+        , fCachedFramebuffer(nullptr)
         , fCachedSimpleRenderPass(nullptr) {
     SkASSERT(info.fProtected == msaaInfo.fProtected);
     SkASSERT(sampleCnt > 1);
-    this->createFramebuffer(gpu);
 }
 
 // We're virtually derived from GrSurface (via GrRenderTarget) so its
@@ -87,9 +85,8 @@
         , fColorAttachmentView(colorAttachmentView)
         , fMSAAImage(nullptr)
         , fResolveAttachmentView(nullptr)
-        , fFramebuffer(nullptr)
+        , fCachedFramebuffer(nullptr)
         , fCachedSimpleRenderPass(nullptr) {
-    this->createFramebuffer(gpu);
     this->registerWithCacheWrapped(GrWrapCacheable::kNo);
 }
 
@@ -107,9 +104,8 @@
         , fColorAttachmentView(colorAttachmentView)
         , fMSAAImage(nullptr)
         , fResolveAttachmentView(nullptr)
-        , fFramebuffer(nullptr)
+        , fCachedFramebuffer(nullptr)
         , fCachedSimpleRenderPass(nullptr) {
-    this->createFramebuffer(gpu);
 }
 
 GrVkRenderTarget::GrVkRenderTarget(GrVkGpu* gpu,
@@ -124,7 +120,7 @@
         , fColorAttachmentView(nullptr)
         , fMSAAImage(nullptr)
         , fResolveAttachmentView(nullptr)
-        , fFramebuffer(nullptr)
+        , fCachedFramebuffer(nullptr)
         , fCachedSimpleRenderPass(renderPass)
         , fSecondaryCommandBuffer(secondaryCommandBuffer) {
     SkASSERT(fSecondaryCommandBuffer != VK_NULL_HANDLE);
@@ -236,30 +232,57 @@
 
 bool GrVkRenderTarget::completeStencilAttachment() {
     SkASSERT(!this->wrapsSecondaryCommandBuffer());
-    this->createFramebuffer(this->getVkGpu());
+    // If we have a previous renderpass or framebuffer it will have been made without stencil, so
+    // we set it to null to trigger creating a new one the next time we need it.
+    if (fCachedSimpleRenderPass) {
+        fCachedSimpleRenderPass->unref(this->getVkGpu());
+        fCachedSimpleRenderPass = nullptr;
+    }
+    if (fCachedFramebuffer) {
+        fCachedFramebuffer->unref(this->getVkGpu());
+        fCachedFramebuffer = nullptr;
+    }
+    fCompatibleRPHandle = GrVkResourceProvider::CompatibleRPHandle();
     return true;
 }
 
-void GrVkRenderTarget::createFramebuffer(GrVkGpu* gpu) {
-    SkASSERT(!this->wrapsSecondaryCommandBuffer());
-    if (fFramebuffer) {
-        fFramebuffer->unref(gpu);
-    }
+const GrVkRenderPass* GrVkRenderTarget::getSimpleRenderPass() {
     if (fCachedSimpleRenderPass) {
-        fCachedSimpleRenderPass->unref(gpu);
+        return fCachedSimpleRenderPass;
     }
+    return this->createSimpleRenderPass();
+}
 
-    // Vulkan requires us to create a compatible renderpass before we can create our framebuffer,
-    // so we use this to get a (cached) basic renderpass, only for creation.
+const GrVkRenderPass* GrVkRenderTarget::createSimpleRenderPass() {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
+    SkASSERT(!fCachedSimpleRenderPass);
+
     fCachedSimpleRenderPass =
-        gpu->resourceProvider().findCompatibleRenderPass(*this, &fCompatibleRPHandle);
+        this->getVkGpu()->resourceProvider().findCompatibleRenderPass(*this, &fCompatibleRPHandle);
+    return fCachedSimpleRenderPass;
+}
 
+const GrVkFramebuffer* GrVkRenderTarget::getFramebuffer() {
+    if (fCachedFramebuffer) {
+        return fCachedFramebuffer;
+    }
+    return this->createFramebuffer();
+}
+
+const GrVkFramebuffer* GrVkRenderTarget::createFramebuffer() {
+    SkASSERT(!this->wrapsSecondaryCommandBuffer());
+    SkASSERT(!fCachedFramebuffer);
+
+    GrVkGpu* gpu = this->getVkGpu();
     // Stencil attachment view is stored in the base RT stencil attachment
     const GrVkImageView* stencilView = this->stencilAttachmentView();
-    fFramebuffer = GrVkFramebuffer::Create(gpu, this->width(), this->height(),
-                                           fCachedSimpleRenderPass, fColorAttachmentView,
-                                           stencilView);
-    SkASSERT(fFramebuffer);
+    const GrVkRenderPass* renderPass = this->getSimpleRenderPass();
+    if (!renderPass) {
+        return nullptr;
+    }
+    fCachedFramebuffer = GrVkFramebuffer::Create(gpu, this->width(), this->height(), renderPass,
+                                                 fColorAttachmentView, stencilView);
+    return fCachedFramebuffer;
 }
 
 void GrVkRenderTarget::getAttachmentsDescriptor(
@@ -276,8 +299,13 @@
         const GrVkStencilAttachment* vkStencil = static_cast<const GrVkStencilAttachment*>(stencil);
         desc->fStencil.fFormat = vkStencil->vkFormat();
         desc->fStencil.fSamples = vkStencil->numSamples();
-        // Currently in vulkan stencil and color attachments must all have same number of samples
-        SkASSERT(desc->fColor.fSamples == desc->fStencil.fSamples);
+#ifdef SK_DEBUG
+        if (this->getVkGpu()->caps()->mixedSamplesSupport()) {
+            SkASSERT(desc->fStencil.fSamples >= desc->fColor.fSamples);
+        } else {
+            SkASSERT(desc->fStencil.fSamples == desc->fColor.fSamples);
+        }
+#endif
         *attachmentFlags |= GrVkRenderPass::kStencil_AttachmentFlag;
         ++attachmentCount;
     }
@@ -289,12 +317,12 @@
     SkASSERT(!fMSAAImage);
     SkASSERT(!fResolveAttachmentView);
     SkASSERT(!fColorAttachmentView);
-    SkASSERT(!fFramebuffer);
+    SkASSERT(!fCachedFramebuffer);
     SkASSERT(!fCachedSimpleRenderPass);
 }
 
-void GrVkRenderTarget::addResources(GrVkCommandBuffer& commandBuffer) const {
-    commandBuffer.addResource(this->framebuffer());
+void GrVkRenderTarget::addResources(GrVkCommandBuffer& commandBuffer) {
+    commandBuffer.addResource(this->getFramebuffer());
     commandBuffer.addResource(this->colorAttachmentView());
     commandBuffer.addResource(this->msaaImageResource() ? this->msaaImageResource()
                                                         : this->resource());
@@ -320,14 +348,19 @@
         fColorAttachmentView->unref(gpu);
         fColorAttachmentView = nullptr;
     }
-    if (fFramebuffer) {
-        fFramebuffer->unref(gpu);
-        fFramebuffer = nullptr;
+    if (fCachedFramebuffer) {
+        fCachedFramebuffer->unref(gpu);
+        fCachedFramebuffer = nullptr;
     }
     if (fCachedSimpleRenderPass) {
         fCachedSimpleRenderPass->unref(gpu);
         fCachedSimpleRenderPass = nullptr;
     }
+    for (int i = 0; i < fGrSecondaryCommandBuffers.count(); ++i) {
+        SkASSERT(fGrSecondaryCommandBuffers[i]);
+        fGrSecondaryCommandBuffers[i]->releaseResources(gpu);
+    }
+    fGrSecondaryCommandBuffers.reset();
 }
 
 void GrVkRenderTarget::abandonInternalObjects() {
@@ -344,14 +377,19 @@
         fColorAttachmentView->unrefAndAbandon();
         fColorAttachmentView = nullptr;
     }
-    if (fFramebuffer) {
-        fFramebuffer->unrefAndAbandon();
-        fFramebuffer = nullptr;
+    if (fCachedFramebuffer) {
+        fCachedFramebuffer->unrefAndAbandon();
+        fCachedFramebuffer = nullptr;
     }
     if (fCachedSimpleRenderPass) {
         fCachedSimpleRenderPass->unrefAndAbandon();
         fCachedSimpleRenderPass = nullptr;
     }
+    for (int i = 0; i < fGrSecondaryCommandBuffers.count(); ++i) {
+        SkASSERT(fGrSecondaryCommandBuffers[i]);
+        fGrSecondaryCommandBuffers[i]->abandonGPUData();
+    }
+    fGrSecondaryCommandBuffers.reset();
 }
 
 void GrVkRenderTarget::onRelease() {
diff --git a/src/gpu/vk/GrVkRenderTarget.h b/src/gpu/vk/GrVkRenderTarget.h
index 9372aa1..0a4cdc3 100644
--- a/src/gpu/vk/GrVkRenderTarget.h
+++ b/src/gpu/vk/GrVkRenderTarget.h
@@ -13,14 +13,13 @@
 #include "src/gpu/vk/GrVkImage.h"
 
 #include "include/gpu/vk/GrVkTypes.h"
+#include "src/gpu/vk/GrVkCommandBuffer.h"
 #include "src/gpu/vk/GrVkRenderPass.h"
 #include "src/gpu/vk/GrVkResourceProvider.h"
 
-class GrVkCommandBuffer;
 class GrVkFramebuffer;
 class GrVkGpu;
 class GrVkImageView;
-class GrVkSecondaryCommandBuffer;
 class GrVkStencilAttachment;
 
 struct GrVkImageInfo;
@@ -44,7 +43,7 @@
 
     GrBackendFormat backendFormat() const override { return this->getBackendFormat(); }
 
-    const GrVkFramebuffer* framebuffer() const { return fFramebuffer; }
+    const GrVkFramebuffer* getFramebuffer();
     const GrVkImageView* colorAttachmentView() const { return fColorAttachmentView; }
     const GrVkResource* msaaImageResource() const {
         if (fMSAAImage) {
@@ -57,9 +56,14 @@
     const GrVkResource* stencilImageResource() const;
     const GrVkImageView* stencilAttachmentView() const;
 
-    const GrVkRenderPass* simpleRenderPass() const { return fCachedSimpleRenderPass; }
-    GrVkResourceProvider::CompatibleRPHandle compatibleRenderPassHandle() const {
+    const GrVkRenderPass* getSimpleRenderPass();
+    GrVkResourceProvider::CompatibleRPHandle compatibleRenderPassHandle() {
         SkASSERT(!this->wrapsSecondaryCommandBuffer());
+        if (!fCompatibleRPHandle.isValid()) {
+            SkASSERT(!fCachedSimpleRenderPass);
+            this->createSimpleRenderPass();
+        }
+        SkASSERT(fCompatibleRPHandle.isValid() == SkToBool(fCachedSimpleRenderPass));
         return fCompatibleRPHandle;
     }
     const GrVkRenderPass* externalRenderPass() const {
@@ -84,7 +88,11 @@
     void getAttachmentsDescriptor(GrVkRenderPass::AttachmentsDescriptor* desc,
                                   GrVkRenderPass::AttachmentFlags* flags) const;
 
-    void addResources(GrVkCommandBuffer& commandBuffer) const;
+    void addResources(GrVkCommandBuffer& commandBuffer);
+
+    void addWrappedGrSecondaryCommandBuffer(std::unique_ptr<GrVkSecondaryCommandBuffer> cmdBuffer) {
+        fGrSecondaryCommandBuffers.push_back(std::move(cmdBuffer));
+    }
 
 protected:
     GrVkRenderTarget(GrVkGpu* gpu,
@@ -105,8 +113,6 @@
                      const GrVkImageView* colorAttachmentView,
                      GrBackendObjectOwnership);
 
-    GrVkGpu* getVkGpu() const;
-
     void onAbandon() override;
     void onRelease() override;
 
@@ -122,12 +128,6 @@
                                       numColorSamples, GrMipMapped::kNo);
     }
 
-    void createFramebuffer(GrVkGpu* gpu);
-
-    const GrVkImageView*       fColorAttachmentView;
-    std::unique_ptr<GrVkImage> fMSAAImage;
-    const GrVkImageView*       fResolveAttachmentView;
-
 private:
     GrVkRenderTarget(GrVkGpu* gpu,
                      const GrSurfaceDesc& desc,
@@ -153,6 +153,11 @@
                      const GrVkRenderPass* renderPass,
                      VkCommandBuffer secondaryCommandBuffer);
 
+    GrVkGpu* getVkGpu() const;
+
+    const GrVkRenderPass* createSimpleRenderPass();
+    const GrVkFramebuffer* createFramebuffer();
+
     bool completeStencilAttachment() override;
 
     // In Vulkan we call the release proc after we are finished with the underlying
@@ -165,7 +170,11 @@
     void releaseInternalObjects();
     void abandonInternalObjects();
 
-    const GrVkFramebuffer*     fFramebuffer;
+    const GrVkImageView*       fColorAttachmentView;
+    std::unique_ptr<GrVkImage> fMSAAImage;
+    const GrVkImageView*       fResolveAttachmentView;
+
+    const GrVkFramebuffer*     fCachedFramebuffer;
 
     // This is a cached pointer to a simple render pass. The render target should unref it
     // once it is done with it.
@@ -177,6 +186,16 @@
     // VkCommandBuffer and not VK_NULL_HANDLE. In this case the render target will not be backed by
     // an actual VkImage and will thus be limited in terms of what it can be used for.
     VkCommandBuffer fSecondaryCommandBuffer = VK_NULL_HANDLE;
+    // When we wrap a secondary command buffer, we will record GrVkResources onto it which need to
+    // be kept alive till the command buffer gets submitted and the GPU has finished. However, in
+    // the wrapped case, we don't know when the command buffer gets submitted and when it is
+    // finished on the GPU since the client is in charge of that. However, we do require that the
+    // client keeps the GrVkSecondaryCBDrawContext alive and call releaseResources on it once the
+    // GPU is finished all the work. Thus we can use this to manage the lifetime of our
+    // GrVkSecondaryCommandBuffers. By storing them on the GrVkRenderTarget, which is owned by the
+    // SkGpuDevice on the GrVkSecondaryCBDrawContext, we assure that the GrVkResources held by the
+    // GrVkSecondaryCommandBuffer don't get deleted before they are allowed to.
+    SkTArray<std::unique_ptr<GrVkCommandBuffer>> fGrSecondaryCommandBuffers;
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkResource.h b/src/gpu/vk/GrVkResource.h
index 7b9949b..6bd4487 100644
--- a/src/gpu/vk/GrVkResource.h
+++ b/src/gpu/vk/GrVkResource.h
@@ -9,6 +9,7 @@
 #define GrVkResource_DEFINED
 
 
+#include "include/private/SkMutex.h"
 #include "include/private/SkTHash.h"
 #include "include/utils/SkRandom.h"
 #include <atomic>
@@ -56,15 +57,18 @@
         }
 
         void add(const GrVkResource* r) {
+            SkAutoMutexExclusive locked(fLock);
             fHashSet.add(r);
         }
 
         void remove(const GrVkResource* r) {
+            SkAutoMutexExclusive locked(fLock);
             fHashSet.remove(r);
         }
 
     private:
-        SkTHashSet<const GrVkResource*, GrVkResource::Hash> fHashSet;
+        SkMutex fLock;
+        SkTHashSet<const GrVkResource*, GrVkResource::Hash> fHashSet SK_GUARDED_BY(fLock);
     };
 
     static std::atomic<uint32_t> fKeyCounter;
diff --git a/src/gpu/vk/GrVkResourceProvider.cpp b/src/gpu/vk/GrVkResourceProvider.cpp
index 5f21dbd..7d0158b 100644
--- a/src/gpu/vk/GrVkResourceProvider.cpp
+++ b/src/gpu/vk/GrVkResourceProvider.cpp
@@ -72,10 +72,10 @@
             createInfo.initialDataSize = 0;
             createInfo.pInitialData = nullptr;
         }
-        VkResult result = GR_VK_CALL(fGpu->vkInterface(),
-                                     CreatePipelineCache(fGpu->device(), &createInfo, nullptr,
-                                                         &fPipelineCache));
-        SkASSERT(VK_SUCCESS == result);
+
+        VkResult result;
+        GR_VK_CALL_RESULT(fGpu, result, CreatePipelineCache(fGpu->device(), &createInfo, nullptr,
+                                                            &fPipelineCache));
         if (VK_SUCCESS != result) {
             fPipelineCache = VK_NULL_HANDLE;
         }
@@ -92,15 +92,13 @@
 }
 
 GrVkPipeline* GrVkResourceProvider::createPipeline(const GrProgramInfo& programInfo,
-                                                   const GrStencilSettings& stencil,
                                                    VkPipelineShaderStageCreateInfo* shaderStageInfo,
                                                    int shaderStageCount,
-                                                   GrPrimitiveType primitiveType,
                                                    VkRenderPass compatibleRenderPass,
                                                    VkPipelineLayout layout) {
-    return GrVkPipeline::Create(fGpu, programInfo, stencil, shaderStageInfo,
-                                shaderStageCount, primitiveType, compatibleRenderPass,
-                                layout, this->pipelineCache());
+    return GrVkPipeline::Create(fGpu, programInfo, shaderStageInfo,
+                                shaderStageCount, compatibleRenderPass, layout,
+                                this->pipelineCache());
 }
 
 // To create framebuffers, we first need to create a simple RenderPass that is
@@ -120,9 +118,11 @@
         }
     }
 
-    const GrVkRenderPass* renderPass =
-        fRenderPassArray.emplace_back(fGpu, target).getCompatibleRenderPass();
-    renderPass->ref();
+    GrVkRenderPass* renderPass = GrVkRenderPass::CreateSimple(fGpu, target);
+    if (!renderPass) {
+        return nullptr;
+    }
+    fRenderPassArray.emplace_back(renderPass);
 
     if (compatibleHandle) {
         *compatibleHandle = CompatibleRPHandle(fRenderPassArray.count() - 1);
@@ -135,6 +135,7 @@
     SkASSERT(compatibleHandle.isValid() && compatibleHandle.toIndex() < fRenderPassArray.count());
     int index = compatibleHandle.toIndex();
     const GrVkRenderPass* renderPass = fRenderPassArray[index].getCompatibleRenderPass();
+    SkASSERT(renderPass);
     renderPass->ref();
     return renderPass;
 }
@@ -160,18 +161,18 @@
 }
 
 const GrVkRenderPass* GrVkResourceProvider::findRenderPass(
-                                                     const GrVkRenderTarget& target,
+                                                     GrVkRenderTarget* target,
                                                      const GrVkRenderPass::LoadStoreOps& colorOps,
                                                      const GrVkRenderPass::LoadStoreOps& stencilOps,
                                                      CompatibleRPHandle* compatibleHandle) {
     GrVkResourceProvider::CompatibleRPHandle tempRPHandle;
     GrVkResourceProvider::CompatibleRPHandle* pRPHandle = compatibleHandle ? compatibleHandle
                                                                            : &tempRPHandle;
-    *pRPHandle = target.compatibleRenderPassHandle();
+    *pRPHandle = target->compatibleRenderPassHandle();
+    if (!pRPHandle->isValid()) {
+        return nullptr;
+    }
 
-    // This will get us the handle to (and possible create) the compatible set for the specific
-    // GrVkRenderPass we are looking for.
-    this->findCompatibleRenderPass(target, compatibleHandle);
     return this->findRenderPass(*pRPHandle, colorOps, stencilOps);
 }
 
@@ -184,13 +185,16 @@
     const GrVkRenderPass* renderPass = compatibleSet.getRenderPass(fGpu,
                                                                    colorOps,
                                                                    stencilOps);
+    if (!renderPass) {
+        return nullptr;
+    }
     renderPass->ref();
     return renderPass;
 }
 
 GrVkDescriptorPool* GrVkResourceProvider::findOrCreateCompatibleDescriptorPool(
                                                             VkDescriptorType type, uint32_t count) {
-    return new GrVkDescriptorPool(fGpu, type, count);
+    return GrVkDescriptorPool::Create(fGpu, type, count);
 }
 
 GrVkSampler* GrVkResourceProvider::findOrCreateCompatibleSampler(
@@ -227,10 +231,8 @@
 GrVkPipelineState* GrVkResourceProvider::findOrCreateCompatiblePipelineState(
         GrRenderTarget* renderTarget,
         const GrProgramInfo& programInfo,
-        GrPrimitiveType primitiveType,
         VkRenderPass compatibleRenderPass) {
-    return fPipelineStateCache->refPipelineState(renderTarget, programInfo,
-                                                 primitiveType, compatibleRenderPass);
+    return fPipelineStateCache->refPipelineState(renderTarget, programInfo, compatibleRenderPass);
 }
 
 void GrVkResourceProvider::getSamplerDescriptorSetHandle(VkDescriptorType type,
@@ -311,6 +313,9 @@
         fAvailableCommandPools.pop_back();
     } else {
         result = GrVkCommandPool::Create(fGpu);
+        if (!result) {
+            return nullptr;
+        }
     }
     SkASSERT(result->unique());
     SkDEBUGCODE(
@@ -510,19 +515,24 @@
 }
 
 void GrVkResourceProvider::storePipelineCacheData() {
+    if (this->pipelineCache() == VK_NULL_HANDLE) {
+        return;
+    }
     size_t dataSize = 0;
-    VkResult result = GR_VK_CALL(fGpu->vkInterface(), GetPipelineCacheData(fGpu->device(),
-                                                                           this->pipelineCache(),
-                                                                           &dataSize, nullptr));
-    SkASSERT(result == VK_SUCCESS);
+    VkResult result;
+    GR_VK_CALL_RESULT(fGpu, result, GetPipelineCacheData(fGpu->device(), this->pipelineCache(),
+                                                         &dataSize, nullptr));
+    if (result != VK_SUCCESS) {
+        return;
+    }
 
     std::unique_ptr<uint8_t[]> data(new uint8_t[dataSize]);
 
-    result = GR_VK_CALL(fGpu->vkInterface(), GetPipelineCacheData(fGpu->device(),
-                                                                  this->pipelineCache(),
-                                                                  &dataSize,
-                                                                  (void*)data.get()));
-    SkASSERT(result == VK_SUCCESS);
+    GR_VK_CALL_RESULT(fGpu, result, GetPipelineCacheData(fGpu->device(), this->pipelineCache(),
+                                                         &dataSize, (void*)data.get()));
+    if (result != VK_SUCCESS) {
+        return;
+    }
 
     uint32_t key = GrVkGpu::kPipelineCache_PersistentCacheKeyType;
     sk_sp<SkData> keyData = SkData::MakeWithoutCopy(&key, sizeof(uint32_t));
@@ -533,16 +543,14 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrVkResourceProvider::CompatibleRenderPassSet::CompatibleRenderPassSet(
-                                                                     const GrVkGpu* gpu,
-                                                                     const GrVkRenderTarget& target)
-    : fLastReturnedIndex(0) {
-    fRenderPasses.emplace_back(new GrVkRenderPass());
-    fRenderPasses[0]->initSimple(gpu, target);
+GrVkResourceProvider::CompatibleRenderPassSet::CompatibleRenderPassSet(GrVkRenderPass* renderPass)
+        : fLastReturnedIndex(0) {
+    renderPass->ref();
+    fRenderPasses.push_back(renderPass);
 }
 
 bool GrVkResourceProvider::CompatibleRenderPassSet::isCompatible(
-                                                             const GrVkRenderTarget& target) const {
+        const GrVkRenderTarget& target) const {
     // The first GrVkRenderpass should always exists since we create the basic load store
     // render pass on create
     SkASSERT(fRenderPasses[0]);
@@ -550,9 +558,9 @@
 }
 
 GrVkRenderPass* GrVkResourceProvider::CompatibleRenderPassSet::getRenderPass(
-                                                   const GrVkGpu* gpu,
-                                                   const GrVkRenderPass::LoadStoreOps& colorOps,
-                                                   const GrVkRenderPass::LoadStoreOps& stencilOps) {
+        GrVkGpu* gpu,
+        const GrVkRenderPass::LoadStoreOps& colorOps,
+        const GrVkRenderPass::LoadStoreOps& stencilOps) {
     for (int i = 0; i < fRenderPasses.count(); ++i) {
         int idx = (i + fLastReturnedIndex) % fRenderPasses.count();
         if (fRenderPasses[idx]->equalLoadStoreOps(colorOps, stencilOps)) {
@@ -560,8 +568,12 @@
             return fRenderPasses[idx];
         }
     }
-    GrVkRenderPass* renderPass = fRenderPasses.emplace_back(new GrVkRenderPass());
-    renderPass->init(gpu, *this->getCompatibleRenderPass(), colorOps, stencilOps);
+    GrVkRenderPass* renderPass = GrVkRenderPass::Create(gpu, *this->getCompatibleRenderPass(),
+                                                        colorOps, stencilOps);
+    if (!renderPass) {
+        return nullptr;
+    }
+    fRenderPasses.push_back(renderPass);
     fLastReturnedIndex = fRenderPasses.count() - 1;
     return renderPass;
 }
diff --git a/src/gpu/vk/GrVkResourceProvider.h b/src/gpu/vk/GrVkResourceProvider.h
index 44755d7..6cdbda8 100644
--- a/src/gpu/vk/GrVkResourceProvider.h
+++ b/src/gpu/vk/GrVkResourceProvider.h
@@ -13,6 +13,7 @@
 #include "src/core/SkLRUCache.h"
 #include "src/core/SkTDynamicHash.h"
 #include "src/core/SkTInternalLList.h"
+#include "src/gpu/GrProgramDesc.h"
 #include "src/gpu/GrResourceHandle.h"
 #include "src/gpu/vk/GrVkDescriptorPool.h"
 #include "src/gpu/vk/GrVkDescriptorSetManager.h"
@@ -44,10 +45,8 @@
     void init();
 
     GrVkPipeline* createPipeline(const GrProgramInfo&,
-                                 const GrStencilSettings& stencil,
                                  VkPipelineShaderStageCreateInfo* shaderStageInfo,
                                  int shaderStageCount,
-                                 GrPrimitiveType primitiveType,
                                  VkRenderPass compatibleRenderPass,
                                  VkPipelineLayout layout);
 
@@ -70,7 +69,7 @@
     // refcount, and returns. The caller can optionally pass in a pointer to a CompatibleRPHandle.
     // If this is non null it will be set to a handle that can be used in the furutre to quickly
     // return a GrVkRenderPasses without the need inspecting a GrVkRenderTarget.
-    const GrVkRenderPass* findRenderPass(const GrVkRenderTarget& target,
+    const GrVkRenderPass* findRenderPass(GrVkRenderTarget* target,
                                          const GrVkRenderPass::LoadStoreOps& colorOps,
                                          const GrVkRenderPass::LoadStoreOps& stencilOps,
                                          CompatibleRPHandle* compatibleHandle = nullptr);
@@ -113,7 +112,6 @@
     GrVkPipelineState* findOrCreateCompatiblePipelineState(
             GrRenderTarget*,
             const GrProgramInfo&,
-            GrPrimitiveType,
             VkRenderPass compatibleRenderPass);
 
     void getSamplerDescriptorSetHandle(VkDescriptorType type,
@@ -194,7 +192,6 @@
         void release();
         GrVkPipelineState* refPipelineState(GrRenderTarget*,
                                             const GrProgramInfo&,
-                                            GrPrimitiveType,
                                             VkRenderPass compatibleRenderPass);
 
     private:
@@ -206,7 +203,7 @@
             }
         };
 
-        SkLRUCache<const GrVkPipelineStateBuilder::Desc, std::unique_ptr<Entry>, DescHash> fMap;
+        SkLRUCache<const GrProgramDesc, std::unique_ptr<Entry>, DescHash> fMap;
 
         GrVkGpu*                    fGpu;
 
@@ -221,7 +218,7 @@
         // This will always construct the basic load store render pass (all attachments load and
         // store their data) so that there is at least one compatible VkRenderPass that can be used
         // with this set.
-        CompatibleRenderPassSet(const GrVkGpu* gpu, const GrVkRenderTarget& target);
+        CompatibleRenderPassSet(GrVkRenderPass* renderPass);
 
         bool isCompatible(const GrVkRenderTarget& target) const;
 
@@ -232,7 +229,7 @@
             return fRenderPasses[0];
         }
 
-        GrVkRenderPass* getRenderPass(const GrVkGpu* gpu,
+        GrVkRenderPass* getRenderPass(GrVkGpu* gpu,
                                       const GrVkRenderPass::LoadStoreOps& colorOps,
                                       const GrVkRenderPass::LoadStoreOps& stencilOps);
 
diff --git a/src/gpu/vk/GrVkSampler.cpp b/src/gpu/vk/GrVkSampler.cpp
index 5dc3544..e83262d 100644
--- a/src/gpu/vk/GrVkSampler.cpp
+++ b/src/gpu/vk/GrVkSampler.cpp
@@ -102,10 +102,12 @@
     }
 
     VkSampler sampler;
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateSampler(gpu->device(),
-                                                          &createInfo,
-                                                          nullptr,
-                                                          &sampler));
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, CreateSampler(gpu->device(), &createInfo, nullptr, &sampler));
+    if (result != VK_SUCCESS) {
+        ycbcrConversion->unref(gpu);
+        return nullptr;
+    }
 
     return new GrVkSampler(sampler, ycbcrConversion, GenerateKey(samplerState, ycbcrInfo));
 }
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
index f33f521..ec0c22d 100644
--- a/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.cpp
@@ -10,7 +10,7 @@
 #include "src/gpu/vk/GrVkGpu.h"
 
 GrVkSamplerYcbcrConversion* GrVkSamplerYcbcrConversion::Create(
-        const GrVkGpu* gpu, const GrVkYcbcrConversionInfo& info) {
+        GrVkGpu* gpu, const GrVkYcbcrConversionInfo& info) {
     if (!gpu->vkCaps().supportsYcbcrConversion()) {
         return nullptr;
     }
@@ -71,9 +71,10 @@
     }
 
     VkSamplerYcbcrConversion conversion;
-    GR_VK_CALL(gpu->vkInterface(), CreateSamplerYcbcrConversion(gpu->device(), &ycbcrCreateInfo,
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, CreateSamplerYcbcrConversion(gpu->device(), &ycbcrCreateInfo,
                                                                 nullptr, &conversion));
-    if (conversion == VK_NULL_HANDLE) {
+    if (result != VK_SUCCESS) {
         return nullptr;
     }
 
diff --git a/src/gpu/vk/GrVkSamplerYcbcrConversion.h b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
index cf7a2c5..5ec3b3d 100644
--- a/src/gpu/vk/GrVkSamplerYcbcrConversion.h
+++ b/src/gpu/vk/GrVkSamplerYcbcrConversion.h
@@ -17,7 +17,7 @@
 
 class GrVkSamplerYcbcrConversion : public GrVkResource {
 public:
-    static GrVkSamplerYcbcrConversion* Create(const GrVkGpu* gpu, const GrVkYcbcrConversionInfo&);
+    static GrVkSamplerYcbcrConversion* Create(GrVkGpu* gpu, const GrVkYcbcrConversionInfo&);
 
     VkSamplerYcbcrConversion ycbcrConversion() const { return fYcbcrConversion; }
 
diff --git a/src/gpu/vk/GrVkSecondaryCBDrawContext.h b/src/gpu/vk/GrVkSecondaryCBDrawContext.h
index b37f6a6..f56ae0a 100644
--- a/src/gpu/vk/GrVkSecondaryCBDrawContext.h
+++ b/src/gpu/vk/GrVkSecondaryCBDrawContext.h
@@ -55,7 +55,7 @@
  * GrVkSecondaryCBDrawContext, the client must call releaseResources() so that Skia can cleanup
  * any internal objects that were created for the draws into the secondary command buffer.
  */
-class SK_API GrVkSecondaryCBDrawContext : public SkRefCnt {
+class SK_SPI GrVkSecondaryCBDrawContext : public SkRefCnt {
 public:
     static sk_sp<GrVkSecondaryCBDrawContext> Make(GrContext*, const SkImageInfo&,
                                                   const GrVkDrawableInfo&,
diff --git a/src/gpu/vk/GrVkSemaphore.cpp b/src/gpu/vk/GrVkSemaphore.cpp
index dd7796e..191b6a3 100644
--- a/src/gpu/vk/GrVkSemaphore.cpp
+++ b/src/gpu/vk/GrVkSemaphore.cpp
@@ -16,54 +16,47 @@
 #undef CreateSemaphore
 #endif
 
-sk_sp<GrVkSemaphore> GrVkSemaphore::Make(GrVkGpu* gpu, bool isOwned) {
+std::unique_ptr<GrVkSemaphore> GrVkSemaphore::Make(GrVkGpu* gpu, bool isOwned) {
     VkSemaphoreCreateInfo createInfo;
     memset(&createInfo, 0, sizeof(VkSemaphoreCreateInfo));
     createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
     createInfo.pNext = nullptr;
     createInfo.flags = 0;
     VkSemaphore semaphore = VK_NULL_HANDLE;
-    GR_VK_CALL_ERRCHECK(gpu->vkInterface(),
-                        CreateSemaphore(gpu->device(), &createInfo, nullptr, &semaphore));
+    VkResult result;
+    GR_VK_CALL_RESULT(gpu, result, CreateSemaphore(gpu->device(), &createInfo, nullptr,
+                                                   &semaphore));
+    if (result != VK_SUCCESS) {
+        return nullptr;
+    }
 
-    return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, false, false, isOwned));
+    return std::unique_ptr<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, false, false, isOwned));
 }
 
-sk_sp<GrVkSemaphore> GrVkSemaphore::MakeWrapped(GrVkGpu* gpu,
-                                                VkSemaphore semaphore,
-                                                WrapType wrapType,
-                                                GrWrapOwnership ownership) {
+std::unique_ptr<GrVkSemaphore> GrVkSemaphore::MakeWrapped(GrVkGpu* gpu,
+                                                          VkSemaphore semaphore,
+                                                          WrapType wrapType,
+                                                          GrWrapOwnership ownership) {
     if (VK_NULL_HANDLE == semaphore) {
         return nullptr;
     }
     bool prohibitSignal = WrapType::kWillWait == wrapType;
     bool prohibitWait = WrapType::kWillSignal == wrapType;
-    return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, prohibitSignal, prohibitWait,
-                                                  kBorrow_GrWrapOwnership != ownership));
+    return std::unique_ptr<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, prohibitSignal,
+                                                            prohibitWait,
+                                                            kBorrow_GrWrapOwnership != ownership));
 }
 
 GrVkSemaphore::GrVkSemaphore(GrVkGpu* gpu, VkSemaphore semaphore, bool prohibitSignal,
                              bool prohibitWait, bool isOwned)
-        : INHERITED(gpu) {
+        : fGpu(gpu) {
     fResource = new Resource(semaphore, prohibitSignal, prohibitWait, isOwned);
-    isOwned ? this->registerWithCache(SkBudgeted::kNo)
-            : this->registerWithCacheWrapped(GrWrapCacheable::kNo);
 }
 
-void GrVkSemaphore::onRelease() {
+GrVkSemaphore::~GrVkSemaphore() {
     if (fResource) {
-        fResource->unref(static_cast<GrVkGpu*>(this->getGpu()));
-        fResource = nullptr;
+        fResource->unref(fGpu);
     }
-    INHERITED::onRelease();
-}
-
-void GrVkSemaphore::onAbandon() {
-    if (fResource) {
-        fResource->unrefAndAbandon();
-        fResource = nullptr;
-    }
-    INHERITED::onAbandon();
 }
 
 void GrVkSemaphore::Resource::freeGPUData(GrVkGpu* gpu) const {
diff --git a/src/gpu/vk/GrVkSemaphore.h b/src/gpu/vk/GrVkSemaphore.h
index 0c73aa1..b18db6d 100644
--- a/src/gpu/vk/GrVkSemaphore.h
+++ b/src/gpu/vk/GrVkSemaphore.h
@@ -19,14 +19,16 @@
 
 class GrVkSemaphore : public GrSemaphore {
 public:
-    static sk_sp<GrVkSemaphore> Make(GrVkGpu* gpu, bool isOwned);
+    static std::unique_ptr<GrVkSemaphore> Make(GrVkGpu* gpu, bool isOwned);
 
     using WrapType = GrResourceProvider::SemaphoreWrapType;
 
-    static sk_sp<GrVkSemaphore> MakeWrapped(GrVkGpu* gpu,
-                                            VkSemaphore semaphore,
-                                            WrapType wrapType,
-                                            GrWrapOwnership);
+    static std::unique_ptr<GrVkSemaphore> MakeWrapped(GrVkGpu* gpu,
+                                                      VkSemaphore semaphore,
+                                                      WrapType wrapType,
+                                                      GrWrapOwnership);
+
+    ~GrVkSemaphore() override;
 
     GrBackendSemaphore backendSemaphore() const override;
 
@@ -57,6 +59,10 @@
             fHasBeenSubmittedToQueueForWait = true;
         }
 
+        void setIsOwned() {
+            fIsOwned = true;
+        }
+
 #ifdef SK_TRACE_VK_RESOURCES
         void dumpInfo() const override {
             SkDebugf("GrVkSemaphore: %d (%d refs)\n", fSemaphore, this->getRefCnt());
@@ -79,11 +85,14 @@
     GrVkSemaphore(GrVkGpu* gpu, VkSemaphore semaphore, bool prohibitSignal, bool prohibitWait,
                   bool isOwned);
 
-    void onRelease() override;
-    void onAbandon() override;
+    void setIsOwned() override {
+        fResource->setIsOwned();
+    }
 
     Resource* fResource;
 
+    GrVkGpu* fGpu;
+
     typedef GrSemaphore INHERITED;
 };
 
diff --git a/src/gpu/vk/GrVkTexture.cpp b/src/gpu/vk/GrVkTexture.cpp
index e162b34..1257c28 100644
--- a/src/gpu/vk/GrVkTexture.cpp
+++ b/src/gpu/vk/GrVkTexture.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/vk/GrVkTexture.h"
 
 #include "src/gpu/GrTexturePriv.h"
+#include "src/gpu/vk/GrVkDescriptorSet.h"
 #include "src/gpu/vk/GrVkGpu.h"
 #include "src/gpu/vk/GrVkImageView.h"
 #include "src/gpu/vk/GrVkTextureRenderTarget.h"
@@ -29,7 +30,8 @@
         , GrVkImage(info, std::move(layout), GrBackendObjectOwnership::kOwned)
         , INHERITED(gpu, {desc.fWidth, desc.fHeight}, desc.fConfig, info.fProtected,
                     GrTextureType::k2D, mipMapsStatus)
-        , fTextureView(view) {
+        , fTextureView(view)
+        , fDescSetCache(kMaxCachedDescSets) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
     // We don't support creating external GrVkTextures
     SkASSERT(!info.fYcbcrConversionInfo.isValid() || !info.fYcbcrConversionInfo.fExternalFormat);
@@ -47,7 +49,8 @@
         , GrVkImage(info, std::move(layout), ownership)
         , INHERITED(gpu, {desc.fWidth, desc.fHeight}, desc.fConfig, info.fProtected,
                     isExternal ? GrTextureType::kExternal : GrTextureType::k2D, mipMapsStatus)
-        , fTextureView(view) {
+        , fTextureView(view)
+        , fDescSetCache(kMaxCachedDescSets) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
     if (ioType == kRead_GrIOType) {
         this->setReadOnly();
@@ -67,7 +70,8 @@
         , GrVkImage(info, layout, ownership)
         , INHERITED(gpu, {desc.fWidth, desc.fHeight}, desc.fConfig, info.fProtected,
                     GrTextureType::k2D, mipMapsStatus)
-        , fTextureView(view) {
+        , fTextureView(view)
+        , fDescSetCache(kMaxCachedDescSets) {
     SkASSERT((GrMipMapsStatus::kNotAllocated == mipMapsStatus) == (1 == info.fLevelCount));
     // Since this ctor is only called from GrVkTextureRenderTarget, we can't have a ycbcr conversion
     // since we don't support that on render targets.
@@ -147,11 +151,26 @@
         fTextureView = nullptr;
     }
 
+    fDescSetCache.reset();
+
     this->releaseImage(this->getVkGpu());
 
     INHERITED::onRelease();
 }
 
+struct GrVkTexture::DescriptorCacheEntry {
+    DescriptorCacheEntry(const GrVkDescriptorSet* fDescSet, GrVkGpu* gpu)
+            : fDescriptorSet(fDescSet), fGpu(gpu) {}
+    ~DescriptorCacheEntry() {
+        if (fDescriptorSet) {
+            fDescriptorSet->recycle(fGpu);
+        }
+    }
+
+    const GrVkDescriptorSet* fDescriptorSet;
+    GrVkGpu* fGpu;
+};
+
 void GrVkTexture::onAbandon() {
     // We're about to be severed from our GrVkResource. If there are "finish" idle procs we have to
     // decide who will handle them. If the resource is still tied to a command buffer we let it
@@ -166,6 +185,12 @@
         fTextureView = nullptr;
     }
 
+    fDescSetCache.foreach ([](std::unique_ptr<DescriptorCacheEntry>* entry) {
+        (*entry)->fDescriptorSet->unrefAndAbandon();
+        (*entry)->fDescriptorSet = nullptr;
+    });
+    fDescSetCache.reset();
+
     this->abandonImage();
     INHERITED::onAbandon();
 }
@@ -250,3 +275,20 @@
     SkASSERT(resourceIdx == resource->idleProcCnt());
     fIdleProcs = procsToKeep;
 }
+
+const GrVkDescriptorSet* GrVkTexture::cachedSingleDescSet(const GrSamplerState& state) {
+    if (std::unique_ptr<DescriptorCacheEntry>* e = fDescSetCache.find(state)) {
+        return (*e)->fDescriptorSet;
+    }
+    return nullptr;
+}
+
+void GrVkTexture::addDescriptorSetToCache(const GrVkDescriptorSet* descSet,
+                                          const GrSamplerState& state) {
+    SkASSERT(!fDescSetCache.find(state));
+    descSet->ref();
+    fDescSetCache.insert(state,
+                         std::unique_ptr<DescriptorCacheEntry>(
+                                 new DescriptorCacheEntry(descSet, this->getVkGpu())));
+}
+
diff --git a/src/gpu/vk/GrVkTexture.h b/src/gpu/vk/GrVkTexture.h
index 6d90826..288b65c2 100644
--- a/src/gpu/vk/GrVkTexture.h
+++ b/src/gpu/vk/GrVkTexture.h
@@ -10,8 +10,11 @@
 
 #include "include/gpu/GrTexture.h"
 #include "include/gpu/vk/GrVkTypes.h"
+#include "src/core/SkLRUCache.h"
+#include "src/gpu/GrSamplerState.h"
 #include "src/gpu/vk/GrVkImage.h"
 
+class GrVkDescriptorSet;
 class GrVkGpu;
 class GrVkImageView;
 struct GrVkImageInfo;
@@ -41,6 +44,15 @@
     void addIdleProc(sk_sp<GrRefCntedCallback>, IdleState) override;
     void callIdleProcsOnBehalfOfResource();
 
+    // For each GrVkTexture, there is a cache of GrVkDescriptorSets which only contain a single
+    // texture/sampler descriptor. If there is a cached descriptor set that matches the passed in
+    // GrSamplerState, then a pointer to it is returned. The ref count is not incremented on the
+    // returned pointer, thus the caller must call ref it if they wish to keep ownership of the
+    // GrVkDescriptorSet.
+    const GrVkDescriptorSet* cachedSingleDescSet(const GrSamplerState&);
+
+    void addDescriptorSetToCache(const GrVkDescriptorSet*, const GrSamplerState&);
+
 protected:
     GrVkTexture(GrVkGpu*, const GrSurfaceDesc&, const GrVkImageInfo&, sk_sp<GrVkImageLayout>,
                 const GrVkImageView*, GrMipMapsStatus, GrBackendObjectOwnership);
@@ -75,6 +87,16 @@
 
     const GrVkImageView* fTextureView;
 
+    struct SamplerHash {
+        uint32_t operator()(const GrSamplerState& state) const {
+            return GrSamplerState::GenerateKey(state);
+        }
+    };
+    struct DescriptorCacheEntry;
+    SkLRUCache<const GrSamplerState, std::unique_ptr<DescriptorCacheEntry>, SamplerHash>
+            fDescSetCache;
+    static constexpr int kMaxCachedDescSets = 8;
+
     typedef GrTexture INHERITED;
 };
 
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index 6df62ce..06bd934 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -254,17 +254,19 @@
     return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
 }
 
-GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(const GrTextureProxy* texture,
+GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler(const GrSurfaceProxy* texture,
                                                                    const GrSamplerState& state,
                                                                    const GrSwizzle& swizzle,
                                                                    const char* name,
                                                                    const GrShaderCaps* shaderCaps) {
     SkASSERT(name && strlen(name));
+    SkASSERT(texture->asTextureProxy());
+
     SkString mangleName;
     char prefix = 'u';
     fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
 
-    GrTextureType type = texture->textureType();
+    GrTextureType type = texture->backendFormat().textureType();
 
     UniformInfo& info = fSamplers.push_back();
     info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
diff --git a/src/gpu/vk/GrVkUniformHandler.h b/src/gpu/vk/GrVkUniformHandler.h
index 76f82f0..33eaa07 100644
--- a/src/gpu/vk/GrVkUniformHandler.h
+++ b/src/gpu/vk/GrVkUniformHandler.h
@@ -77,7 +77,7 @@
         fUniforms[u.toIndex()].fVisibility |= visibility;
     }
 
-    SamplerHandle addSampler(const GrTextureProxy*,
+    SamplerHandle addSampler(const GrSurfaceProxy*,
                              const GrSamplerState&,
                              const GrSwizzle&,
                              const char* name,
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index a49dcea..09ededc 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -96,12 +96,6 @@
         case 16:
             *vkSamples = VK_SAMPLE_COUNT_16_BIT;
             return true;
-        case 32:
-            *vkSamples = VK_SAMPLE_COUNT_32_BIT;
-            return true;
-        case 64:
-            *vkSamples = VK_SAMPLE_COUNT_64_BIT;
-            return true;
         default:
             return false;
     }
@@ -118,7 +112,7 @@
     return SkSL::Program::kFragment_Kind;
 }
 
-bool GrCompileVkShaderModule(const GrVkGpu* gpu,
+bool GrCompileVkShaderModule(GrVkGpu* gpu,
                              const SkSL::String& shaderString,
                              VkShaderStageFlagBits stage,
                              VkShaderModule* shaderModule,
@@ -144,7 +138,7 @@
     return GrInstallVkShaderModule(gpu, *outSPIRV, stage, shaderModule, stageInfo);
 }
 
-bool GrInstallVkShaderModule(const GrVkGpu* gpu,
+bool GrInstallVkShaderModule(GrVkGpu* gpu,
                              const SkSL::String& spirv,
                              VkShaderStageFlagBits stage,
                              VkShaderModule* shaderModule,
@@ -157,10 +151,9 @@
     moduleCreateInfo.codeSize = spirv.size();
     moduleCreateInfo.pCode = (const uint32_t*)spirv.c_str();
 
-    VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateShaderModule(gpu->device(),
-                                                                     &moduleCreateInfo,
-                                                                     nullptr,
-                                                                     shaderModule));
+    VkResult err;
+    GR_VK_CALL_RESULT(gpu, err, CreateShaderModule(gpu->device(), &moduleCreateInfo, nullptr,
+                                                   shaderModule));
     if (err) {
         return false;
     }
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index b34dfbc..0a75427 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -20,14 +20,22 @@
 
 // makes a Vk call on the interface
 #define GR_VK_CALL(IFACE, X) (IFACE)->fFunctions.f##X
+
+#define GR_VK_CALL_RESULT(GPU, RESULT, X)                             \
+    do {                                                              \
+    (RESULT) = GR_VK_CALL(GPU->vkInterface(), X);                     \
+    SkASSERT(VK_SUCCESS == RESULT || VK_ERROR_DEVICE_LOST == RESULT); \
+    if (VK_ERROR_DEVICE_LOST == RESULT) {                             \
+        GPU->setDeviceLost();                                         \
+    }                                                                 \
+    } while(false)
+
+
 // same as GR_VK_CALL but checks for success
-#ifdef SK_DEBUG
-#define GR_VK_CALL_ERRCHECK(IFACE, X)                          \
-    VkResult SK_MACRO_APPEND_LINE(ret) = GR_VK_CALL(IFACE, X); \
-    SkASSERT(VK_SUCCESS == SK_MACRO_APPEND_LINE(ret))
-#else
-#define GR_VK_CALL_ERRCHECK(IFACE, X)  (void) GR_VK_CALL(IFACE, X)
-#endif
+#define GR_VK_CALL_ERRCHECK(GPU, X)                                  \
+    VkResult SK_MACRO_APPEND_LINE(ret);                              \
+    GR_VK_CALL_RESULT(GPU, SK_MACRO_APPEND_LINE(ret), X)             \
+
 
 bool GrVkFormatIsSupported(VkFormat);
 
@@ -42,7 +50,7 @@
 
 bool GrSampleCountToVkSampleCount(uint32_t samples, VkSampleCountFlagBits* vkSamples);
 
-bool GrCompileVkShaderModule(const GrVkGpu* gpu,
+bool GrCompileVkShaderModule(GrVkGpu* gpu,
                              const SkSL::String& shaderString,
                              VkShaderStageFlagBits stage,
                              VkShaderModule* shaderModule,
@@ -51,7 +59,7 @@
                              SkSL::String* outSPIRV,
                              SkSL::Program::Inputs* outInputs);
 
-bool GrInstallVkShaderModule(const GrVkGpu* gpu,
+bool GrInstallVkShaderModule(GrVkGpu* gpu,
                              const SkSL::String& spirv,
                              VkShaderStageFlagBits stage,
                              VkShaderModule* shaderModule,
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 57864bf..675e920 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -93,8 +93,7 @@
 
     GrPaint paint;
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
-    paint.addColorTextureProcessor(std::move(proxy), SkColorTypeToGrColorType(this->colorType()),
-                                   SkMatrix::I());
+    paint.addColorTextureProcessor(std::move(proxy), this->alphaType(), SkMatrix::I());
     if (xform) {
         paint.addColorFragmentProcessor(std::move(xform));
     }
@@ -511,7 +510,7 @@
     context->priv().flushSurface(proxy.get());
     GrGpu* gpu = context->priv().getGpu();
 
-    sk_sp<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get());
+    std::unique_ptr<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get());
 
     auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), proxy->origin(),
                                                     std::move(sema), pixmap->colorType(),
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index e19109e..6d849c9 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -115,11 +115,10 @@
     }
 
     sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef(context);
-    GrColorType srcColorType = SkColorTypeToGrColorType(this->colorType());
 
-    sk_sp<GrTextureProxy> copyProxy = GrSurfaceProxy::Copy(
-            context, proxy.get(), srcColorType, GrMipMapped::kNo, subset, SkBackingFit::kExact,
-            proxy->isBudgeted());
+    sk_sp<GrTextureProxy> copyProxy =
+            GrSurfaceProxy::Copy(context, proxy.get(), GrMipMapped::kNo, subset,
+                                 SkBackingFit::kExact, proxy->isBudgeted());
 
     if (!copyProxy) {
         return nullptr;
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index a04982d..a5c9ac0 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -361,60 +361,6 @@
     return bitmap.peekPixels(&pm) && this->readPixels(pm, srcX, srcY);
 }
 
-// Stuff to keep the legacy async readback APIs working on top of the new implementation.
-namespace {
-struct BridgeContext {
-    SkSurface::ReadPixelsContext fClientContext;
-    SkSurface::LegacyReadPixelsCallback* fClientCallback;
-};
-struct BridgeContextYUV420 {
-    SkSurface::ReadPixelsContext fClientContext;
-    SkSurface::LegacyReadPixelsCallbackYUV420* fClientCallback;
-};
-}  // anonymous namespace
-
-static void bridge_callback(SkSurface::ReadPixelsContext context,
-                            std::unique_ptr<const SkSurface::AsyncReadResult> result) {
-    auto bridgeContext = static_cast<const BridgeContext*>(context);
-    if (!result || result->count() != 1) {
-        bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
-    } else {
-        bridgeContext->fClientCallback(bridgeContext->fClientContext, result->data(0),
-                                       result->rowBytes(0));
-    }
-    delete bridgeContext;
-}
-
-static void bridge_callback_yuv420(SkSurface::ReadPixelsContext context,
-                                   std::unique_ptr<const SkSurface::AsyncReadResult> result) {
-    auto bridgeContext = static_cast<const BridgeContextYUV420*>(context);
-    if (!result || result->count() != 3) {
-        bridgeContext->fClientCallback(bridgeContext->fClientContext, nullptr, 0);
-    } else {
-        const void* data[] = {result->data(0),    result->data(1),     result->data(2)};
-        size_t rowBytes[] = {result->rowBytes(0), result->rowBytes(1), result->rowBytes(2)};
-        bridgeContext->fClientCallback(bridgeContext->fClientContext, data, rowBytes);
-    }
-    delete bridgeContext;
-}
-
-void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
-                                          const SkIRect& srcRect,
-                                          RescaleGamma rescaleGamma,
-                                          SkFilterQuality rescaleQuality,
-                                          LegacyReadPixelsCallback callback,
-                                          ReadPixelsContext context) {
-    if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
-        !SkImageInfoIsValid(info)) {
-        callback(context, nullptr, 0);
-        return;
-    }
-
-    auto bridgeContext = new BridgeContext{context, callback};
-    asSB(this)->onAsyncRescaleAndReadPixels(info, srcRect, rescaleGamma, rescaleQuality,
-                                            bridge_callback, bridgeContext);
-}
-
 void SkSurface::asyncRescaleAndReadPixels(const SkImageInfo& info,
                                           const SkIRect& srcRect,
                                           RescaleGamma rescaleGamma,
@@ -433,29 +379,6 @@
 void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
                                                 sk_sp<SkColorSpace> dstColorSpace,
                                                 const SkIRect& srcRect,
-                                                int dstW, int dstH,
-                                                RescaleGamma rescaleGamma,
-                                                SkFilterQuality rescaleQuality,
-                                                LegacyReadPixelsCallbackYUV420 callback,
-                                                ReadPixelsContext context) {
-    if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || (dstW & 0b1) ||
-        (dstH & 0b1)) {
-        callback(context, nullptr, nullptr);
-        return;
-    }
-    auto bridgeContext = new BridgeContextYUV420{context, callback};
-    asSB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
-                                                  std::move(dstColorSpace), srcRect,
-                                                  {dstW, dstH},
-                                                  rescaleGamma,
-                                                  rescaleQuality,
-                                                  bridge_callback_yuv420,
-                                                  bridgeContext);
-}
-
-void SkSurface::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
-                                                sk_sp<SkColorSpace> dstColorSpace,
-                                                const SkIRect& srcRect,
                                                 const SkISize& dstSize,
                                                 RescaleGamma rescaleGamma,
                                                 SkFilterQuality rescaleQuality,
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 623c54e..6eb3fda 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -104,16 +104,16 @@
     sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef();
 
     if (subset) {
-        srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->colorInfo().colorType(),
-                                        rtc->mipMapped(), *subset, SkBackingFit::kExact, budgeted);
+        srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(), *subset,
+                                        SkBackingFit::kExact, budgeted);
     } else if (!srcProxy || rtc->priv().refsWrappedObjects()) {
         // If the original render target is a buffer originally created by the client, then we don't
         // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
         // copy-on-write.
         SkASSERT(rtc->origin() == rtc->asSurfaceProxy()->origin());
 
-        srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->colorInfo().colorType(),
-                                        rtc->mipMapped(), SkBackingFit::kExact, budgeted);
+        srcProxy = GrSurfaceProxy::Copy(ctx, rtc->asSurfaceProxy(), rtc->mipMapped(),
+                                        SkBackingFit::kExact, budgeted);
     }
 
     const SkImageInfo info = fDevice->imageInfo();
diff --git a/src/image/SkSurface_GpuMtl.mm b/src/image/SkSurface_GpuMtl.mm
index 8739d94..d3e42bd 100644
--- a/src/image/SkSurface_GpuMtl.mm
+++ b/src/image/SkSurface_GpuMtl.mm
@@ -20,7 +20,7 @@
 #if SK_SUPPORT_GPU
 
 #include "include/gpu/GrSurface.h"
-#include "src/gpu/mtl/GrMtlRenderTarget.h"
+#include "src/gpu/mtl/GrMtlTextureRenderTarget.h"
 
 #ifdef SK_METAL
 #import <Metal/Metal.h>
@@ -48,11 +48,14 @@
     }
 
     GrSurfaceDesc desc;
-
     desc.fWidth = metalLayer.drawableSize.width;
     desc.fHeight = metalLayer.drawableSize.height;
     desc.fConfig = config;
 
+    GrProxyProvider::TextureInfo texInfo;
+    texInfo.fMipMapped = GrMipMapped::kNo;
+    texInfo.fTextureType = GrTextureType::k2D;
+
     sk_sp<GrRenderTargetProxy> proxy = proxyProvider->createLazyRenderTargetProxy(
             [layer, drawable, sampleCnt, config](GrResourceProvider* resourceProvider) {
                 CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)layer;
@@ -64,8 +67,15 @@
                 desc.fConfig = config;
 
                 GrMtlGpu* mtlGpu = (GrMtlGpu*) resourceProvider->priv().gpu();
-                auto surface = GrMtlRenderTarget::MakeWrappedRenderTarget(mtlGpu, desc, sampleCnt,
-                                                                          currentDrawable.texture);
+                sk_sp<GrRenderTarget> surface;
+                if (metalLayer.framebufferOnly) {
+                    surface = GrMtlRenderTarget::MakeWrappedRenderTarget(
+                                      mtlGpu, desc, sampleCnt, currentDrawable.texture);
+                } else {
+                    surface = GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(
+                                      mtlGpu, desc, sampleCnt, currentDrawable.texture,
+                                      GrWrapCacheable::kNo);
+                }
                 if (surface && sampleCnt > 1) {
                     surface->setRequiresManualMSAAResolve();
                 }
@@ -79,7 +89,7 @@
             origin,
             sampleCnt > 1 ? GrInternalSurfaceFlags::kRequiresManualMSAAResolve
                           : GrInternalSurfaceFlags::kNone,
-            nullptr, // not textureable
+            metalLayer.framebufferOnly ? nullptr : &texInfo,
             GrMipMapsStatus::kNotAllocated,
             SkBackingFit::kExact,
             SkBudgeted::kYes,
diff --git a/src/images/SkJPEGWriteUtility.h b/src/images/SkJPEGWriteUtility.h
index 356b086..781e9dc 100644
--- a/src/images/SkJPEGWriteUtility.h
+++ b/src/images/SkJPEGWriteUtility.h
@@ -19,13 +19,13 @@
 
 #include <setjmp.h>
 
-void SK_API skjpeg_error_exit(j_common_ptr cinfo);
+void skjpeg_error_exit(j_common_ptr cinfo);
 
 /////////////////////////////////////////////////////////////////////////////
 /* Our destination struct for directing decompressed pixels to our stream
  * object.
  */
-struct SK_API skjpeg_destination_mgr : jpeg_destination_mgr {
+struct SK_SPI skjpeg_destination_mgr : jpeg_destination_mgr {
     skjpeg_destination_mgr(SkWStream* stream);
 
     SkWStream*  fStream;
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 9dac160..8a9eccd 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -36,11 +36,11 @@
 }
 
 bool SkOpBuilder::FixWinding(SkPath* path) {
-    SkPath::FillType fillType = path->getFillType();
-    if (fillType == SkPath::kInverseEvenOdd_FillType) {
-        fillType = SkPath::kInverseWinding_FillType;
-    } else if (fillType == SkPath::kEvenOdd_FillType) {
-        fillType = SkPath::kWinding_FillType;
+    SkPathFillType fillType = path->getNewFillType();
+    if (fillType == SkPathFillType::kInverseEvenOdd) {
+        fillType = SkPathFillType::kInverseWinding;
+    } else if (fillType == SkPathFillType::kEvenOdd) {
+        fillType = SkPathFillType::kWinding;
     }
     SkPathPriv::FirstDirection dir;
     if (one_contour(*path) && SkPathPriv::CheapComputeFirstDirection(*path, &dir)) {
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 3efb0e0..7e97c03 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -10,7 +10,7 @@
 
 void SkOpEdgeBuilder::init() {
     fOperand = false;
-    fXorMask[0] = fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
+    fXorMask[0] = fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
     fUnparseable = false;
     fSecondHalf = preFetch();
@@ -40,7 +40,7 @@
     SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb);
     fPathVerbs.pop();
     fPath = &path;
-    fXorMask[1] = (fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
+    fXorMask[1] = ((int)fPath->getFillType() & 1) ? kEvenOdd_PathOpsMask
             : kWinding_PathOpsMask;
     preFetch();
 }
diff --git a/src/pathops/SkPathOpsAsWinding.cpp b/src/pathops/SkPathOpsAsWinding.cpp
index 80e52be..3f7050d 100644
--- a/src/pathops/SkPathOpsAsWinding.cpp
+++ b/src/pathops/SkPathOpsAsWinding.cpp
@@ -13,7 +13,7 @@
 using std::vector;
 
 struct Contour {
-    enum class Direction {  // SkPath::Direction doesn't have 'none' state
+    enum class Direction {  // SkPathDirection doesn't have 'none' state
         kCCW = -1,
         kNone,
         kCW,
@@ -367,23 +367,23 @@
     const SkPath& fPath;
 };
 
-static bool set_result_path(SkPath* result, const SkPath& path, SkPath::FillType fillType) {
+static bool set_result_path(SkPath* result, const SkPath& path, SkPathFillType fillType) {
     *result = path;
     result->setFillType(fillType);
     return true;
 }
 
-bool SK_API AsWinding(const SkPath& path, SkPath* result) {
+bool AsWinding(const SkPath& path, SkPath* result) {
     if (!path.isFinite()) {
         return false;
     }
-    SkPath::FillType fillType = path.getFillType();
-    if (fillType == SkPath::kWinding_FillType
-            || fillType == SkPath::kInverseWinding_FillType ) {
+    SkPathFillType fillType = path.getNewFillType();
+    if (fillType == SkPathFillType::kWinding
+            || fillType == SkPathFillType::kInverseWinding ) {
         return set_result_path(result, path, fillType);
     }
-    fillType = path.isInverseFillType() ? SkPath::kInverseWinding_FillType :
-            SkPath::kWinding_FillType;
+    fillType = path.isInverseFillType() ? SkPathFillType::kInverseWinding :
+            SkPathFillType::kWinding;
     if (path.isEmpty() || path.isConvex()) {
         return set_result_path(result, path, fillType);
     }
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 7ee44de..b610e9b 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -2901,10 +2901,10 @@
 }
 
 static const char* gFillTypeStr[] = {
-    "kWinding_FillType",
-    "kEvenOdd_FillType",
-    "kInverseWinding_FillType",
-    "kInverseEvenOdd_FillType"
+    "kWinding",
+    "kEvenOdd",
+    "kInverseWinding",
+    "kInverseEvenOdd"
 };
 
 void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
@@ -2914,25 +2914,25 @@
     int rectCount = path.isRectContours() ? path.rectContours(nullptr, nullptr) : 0;
     if (rectCount > 0) {
         SkTDArray<SkRect> rects;
-        SkTDArray<SkPath::Direction> directions;
+        SkTDArray<SkPathDirection> directions;
         rects.setCount(rectCount);
         directions.setCount(rectCount);
         path.rectContours(rects.begin(), directions.begin());
         for (int contour = 0; contour < rectCount; ++contour) {
             const SkRect& rect = rects[contour];
             SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
-                    rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
-                    ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
+                    rect.fRight, rect.fBottom, directions[contour] == SkPathDirection::kCCW
+                    ? "SkPathDirection::kCCW" : "SkPathDirection::kCW");
         }
         return;
     }
 #endif
-    SkPath::FillType fillType = path.getFillType();
-    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
+    SkPathFillType fillType = path.getNewFillType();
+    SkASSERT(fillType >= SkPathFillType::kWinding && fillType <= SkPathFillType::kInverseEvenOdd);
     if (includeDeclaration) {
         SkDebugf("    SkPath %s;\n", name);
     }
-    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
+    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[(int)fillType]);
     iter.setPath(path);
     showPathContours(iter, name);
 }
diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp
index 825ae7b1..2c89f35 100644
--- a/src/pathops/SkPathOpsOp.cpp
+++ b/src/pathops/SkPathOpsOp.cpp
@@ -248,8 +248,8 @@
 #endif
     op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
     bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
-    SkPath::FillType fillType = inverseFill ? SkPath::kInverseEvenOdd_FillType :
-            SkPath::kEvenOdd_FillType;
+    SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd :
+            SkPathFillType::kEvenOdd;
     SkRect rect1, rect2;
     if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
         result->reset();
diff --git a/src/pathops/SkPathOpsSimplify.cpp b/src/pathops/SkPathOpsSimplify.cpp
index f079b50..6c522f4 100644
--- a/src/pathops/SkPathOpsSimplify.cpp
+++ b/src/pathops/SkPathOpsSimplify.cpp
@@ -140,8 +140,8 @@
 bool SimplifyDebug(const SkPath& path, SkPath* result
         SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
     // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
-    SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
-            : SkPath::kEvenOdd_FillType;
+    SkPathFillType fillType = path.isInverseFillType() ? SkPathFillType::kInverseEvenOdd
+            : SkPathFillType::kEvenOdd;
     if (path.isConvex()) {
         if (result != &path) {
             *result = path;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index e58642f..7e88509 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -506,7 +506,7 @@
             maskDevice->makeFormXObjectFromDevice(dstMaskBounds, true), false,
             SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
     SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream());
-    SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream());
+    SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getNewFillType(), content.stream());
     this->clearMaskOnGraphicState(content.stream());
 }
 
@@ -593,7 +593,7 @@
             paint->getStrokeCap() != SkPaint::kSquare_Cap);
     SkPDFUtils::EmitPath(*pathPtr, paint->getStyle(), consumeDegeratePathSegments, content.stream(),
                          tolerance);
-    SkPDFUtils::PaintPath(paint->getStyle(), pathPtr->getFillType(), content.stream());
+    SkPDFUtils::PaintPath(paint->getStyle(), pathPtr->getNewFillType(), content.stream());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1561,7 +1561,7 @@
                 maskDevice->makeFormXObjectFromDevice(maskDeviceBounds, true), false,
                 SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
         SkPDFUtils::AppendRectangle(SkRect::Make(this->size()), content.stream());
-        SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kWinding_FillType, content.stream());
+        SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kWinding, content.stream());
         this->clearMaskOnGraphicState(content.stream());
         return;
     }
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index c027523..f1c0142 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -442,9 +442,9 @@
     sk_sp<SkImage> fImage;
     SkIPoint fOffset;
 };
-static ImageAndOffset to_image(SkGlyphID gid, SkStrike* cache) {
-    (void)cache->prepareImage(cache->glyph(gid));
-    SkMask mask = cache->glyph(gid)->mask();
+static ImageAndOffset to_image(SkGlyphID gid, SkBulkGlyphMetricsAndImages* smallGlyphs) {
+    const SkGlyph* glyph = smallGlyphs->glyph(SkPackedGlyphID{gid});
+    SkMask mask = glyph->mask();
     if (!mask.fImage) {
         return {nullptr, {0, 0}};
     }
@@ -484,7 +484,7 @@
 
 static SkPDFIndirectReference type3_descriptor(SkPDFDocument* doc,
                                                const SkTypeface* typeface,
-                                               SkStrike* cache) {
+                                               SkScalar xHeight) {
     if (SkPDFIndirectReference* ptr = doc->fType3FontDescriptors.find(typeface->uniqueID())) {
         return *ptr;
     }
@@ -500,7 +500,6 @@
         // to "greatly help our workflow downstream".
         if (metrics->fCapHeight != 0) { descriptor.insertInt("CapHeight", metrics->fCapHeight); }
         if (metrics->fStemV     != 0) { descriptor.insertInt("StemV",     metrics->fStemV);     }
-        SkScalar xHeight = cache->getFontMetrics().fXHeight;
         if (xHeight != 0) {
             descriptor.insertScalar("XHeight", xHeight);
         }
@@ -544,11 +543,13 @@
     auto cache = strikeSpec.findOrCreateExclusiveStrike();
     SkASSERT(cache);
     SkScalar emSize = (SkScalar)unitsPerEm;
+    SkScalar xHeight = cache->getFontMetrics().fXHeight;
+    SkBulkGlyphMetricsAndPaths metricsAndPaths(std::move(cache));
 
     SkStrikeSpec strikeSpecSmall = kBitmapFontSize > 0 ? make_small_strike(*typeface)
                                                        : strikeSpec;
-    auto smallCache = strikeSpecSmall.findOrCreateExclusiveStrike();
-    SkASSERT(smallCache);
+
+    SkBulkGlyphMetricsAndImages smallGlyphs(strikeSpecSmall);
     float bitmapScale = kBitmapFontSize > 0 ? emSize / kBitmapFontSize : 1.0f;
 
     SkPDFDict font("Font");
@@ -586,18 +587,18 @@
             characterName.set("g0");
         } else {
             characterName.printf("g%X", gID);
-            SkGlyph* glyph = cache->glyph(gID);
+            const SkGlyph* glyph = metricsAndPaths.glyph(gID);
             advance = glyph->advanceX();
             glyphBBox = glyph->iRect();
             bbox.join(glyphBBox);
-            const SkPath* path = cache->preparePath(glyph);
+            const SkPath* path = glyph->path();
             SkDynamicMemoryWStream content;
             if (path && !path->isEmpty()) {
                 setGlyphWidthAndBoundingBox(glyph->advanceX(), glyphBBox, &content);
                 SkPDFUtils::EmitPath(*path, SkPaint::kFill_Style, &content);
-                SkPDFUtils::PaintPath(SkPaint::kFill_Style, path->getFillType(), &content);
+                SkPDFUtils::PaintPath(SkPaint::kFill_Style, path->getNewFillType(), &content);
             } else {
-                auto pimg = to_image(gID, smallCache.get());
+                auto pimg = to_image(gID, &smallGlyphs);
                 if (!pimg.fImage) {
                     setGlyphWidthAndBoundingBox(glyph->advanceX(), glyphBBox, &content);
                 } else {
@@ -658,7 +659,7 @@
                                                 firstGlyphID,
                                                 lastGlyphID);
     font.insertRef("ToUnicode", SkPDFStreamOut(nullptr, std::move(toUnicodeCmap), doc));
-    font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, cache.get()));
+    font.insertRef("FontDescriptor", type3_descriptor(doc, typeface, xHeight));
     font.insertObject("Widths", std::move(widthArray));
     font.insertObject("Encoding", std::move(encoding));
     font.insertObject("CharProcs", std::move(charProcs));
diff --git a/src/pdf/SkPDFGradientShader.cpp b/src/pdf/SkPDFGradientShader.cpp
index 919c07a5..ad2a65d 100644
--- a/src/pdf/SkPDFGradientShader.cpp
+++ b/src/pdf/SkPDFGradientShader.cpp
@@ -780,7 +780,7 @@
     }
     SkPDFUtils::ApplyPattern(patternIndex, &content);
     SkPDFUtils::AppendRectangle(bounds, &content);
-    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, &content);
+    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kEvenOdd, &content);
     return content.detachAsStream();
 }
 
diff --git a/src/pdf/SkPDFGraphicStackState.cpp b/src/pdf/SkPDFGraphicStackState.cpp
index a27170b..af2fa19 100644
--- a/src/pdf/SkPDFGraphicStackState.cpp
+++ b/src/pdf/SkPDFGraphicStackState.cpp
@@ -104,10 +104,10 @@
 
 static void append_clip_path(const SkPath& clipPath, SkWStream* wStream) {
     SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, wStream);
-    SkPath::FillType clipFill = clipPath.getFillType();
-    NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
-    NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
-    if (clipFill == SkPath::kEvenOdd_FillType) {
+    SkPathFillType clipFill = clipPath.getNewFillType();
+    NOT_IMPLEMENTED(clipFill == SkPathFillType::kInverseEvenOdd, false);
+    NOT_IMPLEMENTED(clipFill == SkPathFillType::kInverseWinding, false);
+    if (clipFill == SkPathFillType::kEvenOdd) {
         wStream->writeText("W* n\n");
     } else {
         wStream->writeText("W n\n");
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index 9beedfb..f00362e 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -128,11 +128,11 @@
 
     SkRect rect;
     bool isClosed; // Both closure and direction need to be checked.
-    SkPath::Direction direction;
+    SkPathDirection direction;
     if (path.isRect(&rect, &isClosed, &direction) &&
         isClosed &&
-        (SkPath::kCW_Direction == direction ||
-         SkPath::kEvenOdd_FillType == path.getFillType()))
+        (SkPathDirection::kCW == direction ||
+         SkPathFillType::kEvenOdd == path.getNewFillType()))
     {
         SkPDFUtils::AppendRectangle(rect, content);
         return;
@@ -213,8 +213,7 @@
     content->writeText("h\n");
 }
 
-void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
-                           SkWStream* content) {
+void SkPDFUtils::PaintPath(SkPaint::Style style, SkPathFillType fill, SkWStream* content) {
     if (style == SkPaint::kFill_Style) {
         content->writeText("f");
     } else if (style == SkPaint::kStrokeAndFill_Style) {
@@ -224,9 +223,9 @@
     }
 
     if (style != SkPaint::kStroke_Style) {
-        NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
-        NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
-        if (fill == SkPath::kEvenOdd_FillType) {
+        NOT_IMPLEMENTED(fill == SkPathFillType::kInverseEvenOdd, false);
+        NOT_IMPLEMENTED(fill == SkPathFillType::kInverseWinding, false);
+        if (fill == SkPathFillType::kEvenOdd) {
             content->writeText("*");
         }
     }
@@ -234,8 +233,7 @@
 }
 
 void SkPDFUtils::StrokePath(SkWStream* content) {
-    SkPDFUtils::PaintPath(
-        SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
+    SkPDFUtils::PaintPath(SkPaint::kStroke_Style, SkPathFillType::kWinding, content);
 }
 
 void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
index 90b1c57..d4c9845 100644
--- a/src/pdf/SkPDFUtils.h
+++ b/src/pdf/SkPDFUtils.h
@@ -58,8 +58,7 @@
     SkPDFUtils::EmitPath(path, paintStyle, true, content, tolerance);
 }
 void ClosePath(SkWStream* content);
-void PaintPath(SkPaint::Style style, SkPath::FillType fill,
-                      SkWStream* content);
+void PaintPath(SkPaint::Style style, SkPathFillType fill, SkWStream* content);
 void StrokePath(SkWStream* content);
 void ApplyGraphicState(int objectIndex, SkWStream* content);
 void ApplyPattern(int objectIndex, SkWStream* content);
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index da5bbcd..3a997ec 100644
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -386,13 +386,12 @@
                 CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
                                       colorspace.get(), BITMAP_INFO_RGB));
 
-        SkUniqueCFRef<CGDataProviderRef> data(
-                CGDataProviderCreateWithData(nullptr, kSpiderSymbol_ttf,
-                                             SK_ARRAY_COUNT(kSpiderSymbol_ttf), nullptr));
-        SkUniqueCFRef<CGFontRef> cgFont(CGFontCreateWithDataProvider(data.get()));
-        SkASSERT(cgFont);
-        SkUniqueCFRef<CTFontRef> ctFont(
-                CTFontCreateWithGraphicsFont(cgFont.get(), 16, nullptr, nullptr));
+        SkUniqueCFRef<CFDataRef> data(CFDataCreateWithBytesNoCopy(
+                kCFAllocatorDefault, kSpiderSymbol_ttf, SK_ARRAY_COUNT(kSpiderSymbol_ttf),
+                kCFAllocatorNull));
+        SkUniqueCFRef<CTFontDescriptorRef> desc(
+                CTFontManagerCreateFontDescriptorFromData(data.get()));
+        SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc.get(), 16, nullptr));
         SkASSERT(ctFont);
 
         CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
@@ -695,14 +694,20 @@
                              : SkFontStyle::kUpright_Slant);
 }
 
+struct OpszVariation {
+    bool isSet = false;
+    double value = 0;
+};
+
 class SkTypeface_Mac : public SkTypeface {
 public:
     SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, SkUniqueCFRef<CFTypeRef> resourceRef,
-                   const SkFontStyle& fs, bool isFixedPitch,
+                   const SkFontStyle& fs, bool isFixedPitch, OpszVariation opszVariation,
                    std::unique_ptr<SkStreamAsset> providedData)
         : SkTypeface(fs, isFixedPitch)
         , fFontRef(std::move(fontRef))
         , fOriginatingCFTypeRef(std::move(resourceRef))
+        , fOpszVariation(opszVariation)
         , fHasColorGlyphs(
                 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
         , fStream(std::move(providedData))
@@ -713,6 +718,7 @@
 
     SkUniqueCFRef<CTFontRef> fFontRef;
     SkUniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
+    const OpszVariation fOpszVariation;
     const bool fHasColorGlyphs;
 
 protected:
@@ -764,6 +770,7 @@
 /** Creates a typeface, searching the cache if isLocalStream is false. */
 static sk_sp<SkTypeface> create_from_CTFontRef(SkUniqueCFRef<CTFontRef> font,
                                                SkUniqueCFRef<CFTypeRef> resource,
+                                               OpszVariation opszVariation,
                                                std::unique_ptr<SkStreamAsset> providedData) {
     SkASSERT(font);
     const bool isFromStream(providedData);
@@ -782,7 +789,8 @@
     bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
 
     sk_sp<SkTypeface> face(new SkTypeface_Mac(std::move(font), std::move(resource),
-                                              style, isFixedPitch, std::move(providedData)));
+                                              style, isFixedPitch, opszVariation,
+                                              std::move(providedData)));
     if (!isFromStream) {
         SkTypefaceCache::Add(face);
     }
@@ -796,7 +804,7 @@
         return nullptr;
     }
 
-    return create_from_CTFontRef(std::move(ctFont), nullptr, nullptr);
+    return create_from_CTFontRef(std::move(ctFont), nullptr, OpszVariation(), nullptr);
 }
 
 static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
@@ -875,13 +883,14 @@
     }
 
     if (expected_traits != traits) {
-        SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(ctFont.get(), 0,                                       nullptr, expected_traits, expected_traits));
+        SkUniqueCFRef<CTFontRef> ctNewFont(CTFontCreateCopyWithSymbolicTraits(
+                    ctFont.get(), 0, nullptr, expected_traits, expected_traits));
         if (ctNewFont) {
             ctFont = std::move(ctNewFont);
         }
     }
 
-    return create_from_CTFontRef(std::move(ctFont), nullptr, nullptr);
+    return create_from_CTFontRef(std::move(ctFont), nullptr, OpszVariation(), nullptr);
 }
 
 /** Creates a typeface from a name, searching the cache. */
@@ -905,6 +914,7 @@
     }
     return create_from_CTFontRef(SkUniqueCFRef<CTFontRef>(font),
                                  SkUniqueCFRef<CFTypeRef>(resource),
+                                 OpszVariation(),
                                  nullptr).release();
 }
 
@@ -992,25 +1002,53 @@
     typedef SkScalerContext INHERITED;
 };
 
+// In macOS 10.12 and later any variation on the CGFont which has default axis value will be
+// dropped when creating the CTFont. Unfortunately, in macOS 10.15 the priority of setting
+// the optical size (and opsz variation) is
+// 1. the value of kCTFontOpticalSizeAttribute in the CTFontDescriptor (undocumented)
+// 2. the opsz axis default value if kCTFontOpticalSizeAttribute is 'none' (undocumented)
+// 3. the opsz variation on the nascent CTFont from the CGFont (was dropped if default)
+// 4. the opsz variation in kCTFontVariationAttribute in CTFontDescriptor (crashes 10.10)
+// 5. the size requested (can fudge in SkTypeface but not SkScalerContext)
+// The first one which is found will be used to set the opsz variation (after clamping).
+static SkUniqueCFRef<CTFontDescriptorRef> create_opsz_descriptor(double opsz) {
+    SkUniqueCFRef<CFMutableDictionaryRef> attr(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 1,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+    SkUniqueCFRef<CFNumberRef> opszValueNumber(
+        CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &opsz));
+    // Avoid using kCTFontOpticalSizeAttribute directly
+    CFStringRef SkCTFontOpticalSizeAttribute = CFSTR("NSCTFontOpticalSizeAttribute");
+    CFDictionarySetValue(attr.get(), SkCTFontOpticalSizeAttribute, opszValueNumber.get());
+    return SkUniqueCFRef<CTFontDescriptorRef>(CTFontDescriptorCreateWithAttributes(attr.get()));
+}
+
 // CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
 // and later, as they will return different underlying fonts depending on the size requested.
 // It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
 // work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
 // CGFont.
-static SkUniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize)
+static SkUniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
+                                                         OpszVariation opsz)
 {
     SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
 
-    // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
-    // If non-nullptr then with fonts with variation axes, the copy will fail in
+    // The last parameter (CTFontDescriptorRef attributes) *must* not set variations.
+    // If it sets variations then with fonts with variation axes the copy will fail in
     // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
     // which it quite obviously is not.
 
     // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
     // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
     // the scaler context, since they aren't 'normal'.
+
+    SkUniqueCFRef<CTFontDescriptorRef> desc;
+    if (opsz.isSet) {
+        desc = create_opsz_descriptor(opsz.value);
+    }
     return SkUniqueCFRef<CTFontRef>(
-            CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, nullptr));
+            CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, nullptr, desc.get()));
 }
 
 SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
@@ -1043,7 +1081,8 @@
     // The transform contains everything except the requested text size.
     // Some properties, like 'trak', are based on the text size (before applying the matrix).
     CGFloat textSize = ScalarToCG(scale.y());
-    fCTFont = ctfont_create_exact_copy(ctFont, textSize);
+    fCTFont = ctfont_create_exact_copy(ctFont, textSize,
+                                       ((SkTypeface_Mac*)this->getTypeface())->fOpszVariation);
     fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
 }
 
@@ -1246,8 +1285,6 @@
     glyph->fHeight = SkToU16(skIBounds.height());
 }
 
-#include "include/private/SkColorData.h"
-
 static constexpr uint8_t sk_pow2_table(size_t i) {
     return SkToU8(((i * i + 128) / 255));
 }
@@ -1579,25 +1616,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// Returns nullptr on failure
-// Call must still manage its ownership of provider
-static sk_sp<SkTypeface> create_from_dataProvider(SkUniqueCFRef<CGDataProviderRef> provider,
-                                                  std::unique_ptr<SkStreamAsset> providedData,
-                                                  int ttcIndex) {
-    if (ttcIndex != 0) {
-        return nullptr;
-    }
-    SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
-    if (!cg) {
-        return nullptr;
-    }
-    SkUniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
-    if (!ct) {
-        return nullptr;
-    }
-    return create_from_CTFontRef(std::move(ct), nullptr, std::move(providedData));
-}
-
 // Web fonts added to the CTFont registry do not return their character set.
 // Iterate through the font in this case. The existing caller caches the result,
 // so the performance impact isn't too bad.
@@ -1719,7 +1737,8 @@
 
 void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
     SkUniqueCFRef<CTFontRef> ctFont =
-            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()));
+            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+                                     fOpszVariation);
     CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
     populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
 }
@@ -1727,7 +1746,8 @@
 std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
 
     SkUniqueCFRef<CTFontRef> ctFont =
-            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()));
+            ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()),
+                                     fOpszVariation);
 
     std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
 
@@ -2076,7 +2096,7 @@
 static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
                                                                      CFArrayRef ctAxes) {
 
-    SkUniqueCFRef<CFMutableDictionaryRef> ctVariations(
+    SkUniqueCFRef<CFMutableDictionaryRef> ctVariation(
             CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                       &kCFTypeDictionaryKeyCallBacks,
                                       &kCFTypeDictionaryValueCallBacks));
@@ -2106,9 +2126,9 @@
             return nullptr;
         }
 
-        CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
+        CFDictionaryAddValue(ctVariation.get(), axisTag, axisValue);
     }
-    return ctVariations;
+    return ctVariation;
 }
 
 int SkTypeface_Mac::onGetVariationDesignPosition(
@@ -2130,8 +2150,8 @@
     // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
     // When this happens, try converting the CG variation to a CT variation.
     // On 10.12 and later, this only returns non-default variations.
-    SkUniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
-    if (!ctVariations) {
+    SkUniqueCFRef<CFDictionaryRef> ctVariation(CTFontCopyVariation(fFontRef.get()));
+    if (!ctVariation) {
         // When 10.11 and earlier are no longer supported, the following code can be replaced with
         // return -1 and ct_variation_from_cg_variation can be removed.
         SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
@@ -2142,8 +2162,8 @@
         if (!cgVariations) {
             return -1;
         }
-        ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
-        if (!ctVariations) {
+        ctVariation = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
+        if (!ctVariation) {
             return -1;
         }
     }
@@ -2167,7 +2187,7 @@
         coordinates[i].axis = tagLong;
 
         CGFloat variationCGFloat;
-        CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
+        CFTypeRef variationValue = CFDictionaryGetValue(ctVariation.get(), tagNumber);
         if (variationValue) {
             if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
                 return -1;
@@ -2476,8 +2496,6 @@
     return true;
 }
 
-#include "include/core/SkFontMgr.h"
-
 static inline int sqr(int value) {
     SkASSERT(SkAbs32(value) < 0x7FFF);  // check for overflow
     return value * value;
@@ -2649,7 +2667,8 @@
         CFRange range = CFRangeMake(0, CFStringGetLength(string.get()));  // in UniChar units.
         SkUniqueCFRef<CTFontRef> fallbackFont(
                 CTFontCreateForString(familyFont.get(), string.get(), range));
-        return create_from_CTFontRef(std::move(fallbackFont), nullptr, nullptr).release();
+        return create_from_CTFontRef(std::move(fallbackFont), nullptr,
+                                     OpszVariation(), nullptr).release();
     }
 
     SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
@@ -2658,36 +2677,52 @@
     }
 
     sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
-        SkUniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
-        if (!pr) {
+        if (ttcIndex != 0) {
             return nullptr;
         }
-        return create_from_dataProvider(std::move(pr), SkMemoryStream::Make(std::move(data)),
-                                        ttcIndex);
+
+        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(data, ttcIndex);
+        if (!ct) {
+            return nullptr;
+        }
+
+        return create_from_CTFontRef(std::move(ct), nullptr, OpszVariation(),
+                                     SkMemoryStream::Make(std::move(data)));
     }
 
     sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
                                             int ttcIndex) const override {
-        SkUniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream->duplicate()));
-        if (!pr) {
+        if (ttcIndex != 0) {
             return nullptr;
         }
-        return create_from_dataProvider(std::move(pr), std::move(stream), ttcIndex);
+
+        sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
+        if (!data) {
+            return nullptr;
+        }
+        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
+        if (!ct) {
+            return nullptr;
+        }
+
+        return create_from_CTFontRef(std::move(ct), nullptr, OpszVariation(), std::move(stream));
     }
 
-    /** Creates a dictionary suitable for setting the axes on a CGFont. */
-    static SkUniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
-        // The CGFont variation data is keyed by name, but lacks the tag.
-        // The CTFont variation data is keyed by tag, and also has the name.
-        // We would like to work with CTFont variations, but creating a CTFont font with
-        // CTFont variation dictionary runs into bugs. So use the CTFont variation data
-        // to match names to tags to create the appropriate CGFont.
-        SkUniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
-        // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
-        // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
-        SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
+    struct CTFontVariation {
+        SkUniqueCFRef<CFDictionaryRef> dict;
+        OpszVariation opsz;
+    };
+
+    /** Creates a dictionary suitable for setting the axes on a CTFont. */
+    static CTFontVariation ctvariation_from_skfontarguments(CTFontRef ct,
+                                                            const SkFontArguments& args)
+    {
+        OpszVariation opsz;
+        constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+        SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
         if (!ctAxes) {
-            return nullptr;
+            return CTFontVariation();
         }
         CFIndex axisCount = CFArrayGetCount(ctAxes.get());
 
@@ -2701,26 +2736,18 @@
         for (int i = 0; i < axisCount; ++i) {
             CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
             if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-                return nullptr;
+                return CTFontVariation();
             }
             CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
 
-            // The assumption is that values produced by kCTFontVariationAxisNameKey and
-            // kCGFontVariationAxisName will always be equal.
-            // If they are ever not, seach the project history for "get_tag_for_name".
-            CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
-            if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
-                return nullptr;
-            }
-
             CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
             if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
-                return nullptr;
+                return CTFontVariation();
             }
             CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
             int64_t tagLong;
             if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
-                return nullptr;
+                return CTFontVariation();
             }
 
             // The variation axes can be set to any value, but cg will effectively pin them.
@@ -2732,7 +2759,7 @@
                 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
                 !def || CFGetTypeID(def) != CFNumberGetTypeID())
             {
-                return nullptr;
+                return CTFontVariation();
             }
             CFNumberRef minNumber = static_cast<CFNumberRef>(min);
             CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
@@ -2744,7 +2771,7 @@
                 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
                 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
             {
-                return nullptr;
+                return CTFontVariation();
             }
 
             double value = defDouble;
@@ -2754,58 +2781,129 @@
                 if (position.coordinates[j].axis == tagLong) {
                     value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
                                    minDouble, maxDouble);
+                    if (tagLong == opszTag) {
+                        opsz.isSet = true;
+                    }
                     break;
                 }
             }
+            if (tagLong == opszTag) {
+                opsz.value = value;
+            }
             SkUniqueCFRef<CFNumberRef> valueNumber(
                 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
-            CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
+            CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
         }
-        return dict;
+        return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
     }
-    sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
-                                           const SkFontArguments& args) const override {
-        if (args.getCollectionIndex() != 0) {
-            return nullptr;
+
+    static sk_sp<SkData> skdata_from_skstreamasset(std::unique_ptr<SkStreamAsset> stream) {
+        size_t size = stream->getLength();
+        if (const void* base = stream->getMemoryBase()) {
+            return SkData::MakeWithProc(base, size,
+                                        [](const void*, void* ctx) -> void {
+                                            delete (SkStreamAsset*)ctx;
+                                        }, stream.release());
         }
-        SkUniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(s->duplicate()));
-        if (!provider) {
-            return nullptr;
-        }
-        SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
-        if (!cg) {
+        return SkData::MakeFromStream(stream.get(), size);
+    }
+
+    static SkUniqueCFRef<CFDataRef> cfdata_from_skdata(sk_sp<SkData> data) {
+        void const * const addr = data->data();
+        size_t const size = data->size();
+
+        CFAllocatorContext ctx = {
+            0, // CFIndex version
+            data.release(), // void* info
+            nullptr, // const void *(*retain)(const void *info);
+            nullptr, // void (*release)(const void *info);
+            nullptr, // CFStringRef (*copyDescription)(const void *info);
+            nullptr, // void * (*allocate)(CFIndex size, CFOptionFlags hint, void *info);
+            nullptr, // void*(*reallocate)(void* ptr,CFIndex newsize,CFOptionFlags hint,void* info);
+            [](void*,void* info) -> void { // void (*deallocate)(void *ptr, void *info);
+                SkASSERT(info);
+                ((SkData*)info)->unref();
+            },
+            nullptr, // CFIndex (*preferredSize)(CFIndex size, CFOptionFlags hint, void *info);
+        };
+        SkUniqueCFRef<CFAllocatorRef> alloc(CFAllocatorCreate(kCFAllocatorDefault, &ctx));
+        return SkUniqueCFRef<CFDataRef>(CFDataCreateWithBytesNoCopy(
+                kCFAllocatorDefault, (const UInt8 *)addr, size, alloc.get()));
+    }
+
+    static SkUniqueCFRef<CTFontRef> ctfont_from_skdata(sk_sp<SkData> data, int ttcIndex) {
+        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+        if (ttcIndex != 0) {
             return nullptr;
         }
 
-        SkUniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
-        // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
-        // created from a data provider does not appear to have any ownership of the underlying
-        // data. The original CGFontRef must be kept alive until the copy will no longer be used.
-        SkUniqueCFRef<CGFontRef> cgVariant;
-        if (cgVariations) {
-            cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
-        } else {
-            cgVariant.reset(cg.release());
+        SkUniqueCFRef<CFDataRef> cfData(cfdata_from_skdata(std::move(data)));
+
+        SkUniqueCFRef<CTFontDescriptorRef> desc(
+                CTFontManagerCreateFontDescriptorFromData(cfData.get()));
+        if (!desc) {
+            return nullptr;
+        }
+        return SkUniqueCFRef<CTFontRef>(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
+    }
+
+    sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> stream,
+                                           const SkFontArguments& args) const override
+    {
+        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
+        int ttcIndex = args.getCollectionIndex();
+        if (ttcIndex != 0) {
+            return nullptr;
         }
 
-        SkUniqueCFRef<CTFontRef> ct(
-                CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
+        sk_sp<SkData> data = skdata_from_skstreamasset(stream->duplicate());
+        if (!data) {
+            return nullptr;
+        }
+        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), ttcIndex);
         if (!ct) {
             return nullptr;
         }
-        return create_from_CTFontRef(std::move(ct), std::move(cg), std::move(s));
-    }
 
-    /** Creates a dictionary suitable for setting the axes on a CGFont. */
-    static SkUniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
-        SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
-        if (!cgAxes) {
+        CTFontVariation ctVariation = ctvariation_from_skfontarguments(ct.get(), args);
+
+        SkUniqueCFRef<CTFontRef> ctVariant;
+        if (ctVariation.dict) {
+            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks));
+            CFDictionaryAddValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.dict.get());
+            SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+                    CTFontDescriptorCreateWithAttributes(attributes.get()));
+            ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
+        } else {
+            ctVariant.reset(ct.release());
+        }
+        if (!ctVariant) {
             return nullptr;
         }
 
-        CFIndex axisCount = CFArrayGetCount(cgAxes.get());
+        return create_from_CTFontRef(std::move(ctVariant), nullptr, ctVariation.opsz,
+                                     std::move(stream));
+    }
+
+    /** Creates a dictionary suitable for setting the axes on a CTFont. */
+    static CTFontVariation ctvariation_from_skfontdata(CTFontRef ct, SkFontData* fontData) {
+        // In macOS 10.15 CTFontCreate* overrides any 'opsz' variation with the 'size'.
+        // Track the 'opsz' and return it, since it is an out of band axis.
+        OpszVariation opsz;
+        constexpr const SkFourByteTag opszTag = SkSetFourByteTag('o','p','s','z');
+
+        SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
+        if (!ctAxes) {
+            return CTFontVariation();
+        }
+
+        CFIndex axisCount = CFArrayGetCount(ctAxes.get());
         if (0 == axisCount || axisCount != fontData->getAxisCount()) {
-            return nullptr;
+            return CTFontVariation();
         }
 
         SkUniqueCFRef<CFMutableDictionaryRef> dict(
@@ -2814,25 +2912,31 @@
                                           &kCFTypeDictionaryValueCallBacks));
 
         for (int i = 0; i < fontData->getAxisCount(); ++i) {
-            CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
+            CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
             if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
-                return nullptr;
+                return CTFontVariation();
             }
             CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
 
-            CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
-            if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
-                return nullptr;
+            CFTypeRef tag = CFDictionaryGetValue(axisInfoDict,
+                                                 kCTFontVariationAxisIdentifierKey);
+            if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
+                return CTFontVariation();
+            }
+            CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
+            int64_t tagLong;
+            if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
+                return CTFontVariation();
             }
 
             // The variation axes can be set to any value, but cg will effectively pin them.
             // Pin them here to normalize.
-            CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
-            CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
+            CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
+            CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
             if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
                 !max || CFGetTypeID(max) != CFNumberGetTypeID())
             {
-                return nullptr;
+                return CTFontVariation();
             }
             CFNumberRef minNumber = static_cast<CFNumberRef>(min);
             CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
@@ -2841,54 +2945,71 @@
             if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
                 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
             {
-                return nullptr;
+                return CTFontVariation();
             }
             double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
+
+            if (tagLong == opszTag) {
+                opsz.isSet = true;
+                opsz.value = value;
+            }
+
             SkUniqueCFRef<CFNumberRef> valueNumber(
                     CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
-            CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
+            CFDictionaryAddValue(dict.get(), tagNumber, valueNumber.get());
         }
-        return dict;
+        return { SkUniqueCFRef<CFDictionaryRef>(std::move(dict)), opsz };
     }
     sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
+        // TODO: Use CTFontManagerCreateFontDescriptorsFromData when available.
         if (fontData->getIndex() != 0) {
             return nullptr;
         }
-        SkUniqueCFRef<CGDataProviderRef> provider(
-                SkCreateDataProviderFromStream(fontData->getStream()->duplicate()));
-        if (!provider) {
+
+        sk_sp<SkData> data = skdata_from_skstreamasset(fontData->getStream()->duplicate());
+        if (!data) {
             return nullptr;
         }
-        SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
-        if (!cg) {
-            return nullptr;
-        }
-
-        SkUniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
-        // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
-        // created from a data provider does not appear to have any ownership of the underlying
-        // data. The original CGFontRef must be kept alive until the copy will no longer be used.
-        SkUniqueCFRef<CGFontRef> cgVariant;
-        if (cgVariations) {
-            cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
-        } else {
-            cgVariant.reset(cg.release());
-        }
-
-        SkUniqueCFRef<CTFontRef> ct(
-                CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
+        SkUniqueCFRef<CTFontRef> ct = ctfont_from_skdata(std::move(data), fontData->getIndex());
         if (!ct) {
             return nullptr;
         }
-        return create_from_CTFontRef(std::move(ct), std::move(cg), fontData->detachStream());
+
+        CTFontVariation ctVariation = ctvariation_from_skfontdata(ct.get(), fontData.get());
+
+        SkUniqueCFRef<CTFontRef> ctVariant;
+        if (ctVariation.dict) {
+            SkUniqueCFRef<CFMutableDictionaryRef> attributes(
+                    CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks));
+            CFDictionaryAddValue(attributes.get(),
+                                 kCTFontVariationAttribute, ctVariation.dict.get());
+            SkUniqueCFRef<CTFontDescriptorRef> varDesc(
+                    CTFontDescriptorCreateWithAttributes(attributes.get()));
+            ctVariant.reset(CTFontCreateCopyWithAttributes(ct.get(), 0, nullptr, varDesc.get()));
+        } else {
+            ctVariant.reset(ct.release());
+        }
+        if (!ctVariant) {
+            return nullptr;
+        }
+
+        return create_from_CTFontRef(std::move(ctVariant), nullptr,
+                                     ctVariation.opsz, fontData->detachStream());
     }
 
     sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
-        SkUniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
-        if (!pr) {
+        if (ttcIndex != 0) {
             return nullptr;
         }
-        return create_from_dataProvider(std::move(pr), SkFILEStream::Make(path), ttcIndex);
+
+        sk_sp<SkData> data = SkData::MakeFromFileName(path);
+        if (!data) {
+            return nullptr;
+        }
+
+        return this->onMakeFromData(std::move(data), ttcIndex);
     }
 
     sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
diff --git a/src/ports/SkTLS_pthread.cpp b/src/ports/SkTLS_pthread.cpp
index c96271b..d017c51 100644
--- a/src/ports/SkTLS_pthread.cpp
+++ b/src/ports/SkTLS_pthread.cpp
@@ -16,7 +16,7 @@
     // should we use forceCreateTheSlot to potentially just return nullptr if
     // we've never been called with forceCreateTheSlot==true ?
     static SkOnce once;
-    once(pthread_key_create, &gSkTLSKey, SkTLS::Destructor);
+    once([]{pthread_key_create(&gSkTLSKey, SkTLS::Destructor);});
     return pthread_getspecific(gSkTLSKey);
 }
 
diff --git a/src/ports/SkTypeface_win_dw.h b/src/ports/SkTypeface_win_dw.h
index 72d86a2..e090727 100644
--- a/src/ports/SkTypeface_win_dw.h
+++ b/src/ports/SkTypeface_win_dw.h
@@ -68,6 +68,10 @@
         if (!SUCCEEDED(fFactory->QueryInterface(&fFactory2))) {
             SkASSERT_RELEASE(nullptr == fFactory2.get());
         }
+
+        if (fDWriteFontFace1 && fDWriteFontFace1->IsMonospacedFont()) {
+            this->setIsFixedPitch(true);
+        }
     }
 
 public:
diff --git a/src/shaders/SkColorFilterShader.cpp b/src/shaders/SkColorFilterShader.cpp
index ded03fe..3ab9098 100644
--- a/src/shaders/SkColorFilterShader.cpp
+++ b/src/shaders/SkColorFilterShader.cpp
@@ -37,6 +37,12 @@
     return sk_make_sp<SkColorFilterShader>(shader, 1.0f, filter);
 }
 
+bool SkColorFilterShader::isOpaque() const {
+    return fShader->isOpaque()
+        && fAlpha == 1.0f
+        && (fFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) != 0;
+}
+
 void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const {
     buffer.writeFlattenable(fShader.get());
     SkASSERT(fAlpha == 1.0f);  // Not exposed in public API SkShader::makeWithColorFilter().
@@ -54,6 +60,33 @@
     return true;
 }
 
+bool SkColorFilterShader::onProgram(skvm::Builder* p,
+                                    SkColorSpace* dstCS,
+                                    skvm::Uniforms* uniforms,
+                                    skvm::F32 x, skvm::F32 y,
+                                    skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    // Run the shader.
+    if (!as_SB(fShader)->program(p, dstCS, uniforms, x,y, r,g,b,a)) {
+        return false;
+    }
+
+    // Scale that by alpha.
+    if (fAlpha != 1.0f) {
+        skvm::F32 A = p->uniformF(uniforms->pushF(fAlpha));
+        *r = p->mul(*r, A);
+        *g = p->mul(*g, A);
+        *b = p->mul(*b, A);
+        *a = p->mul(*a, A);
+    }
+
+    // Finally run that through the color filter.
+    if (!fFilter->program(p, dstCS, uniforms, r,g,b,a)) {
+        return false;
+    }
+
+    return true;
+}
+
 #if SK_SUPPORT_GPU
 /////////////////////////////////////////////////////////////////////
 
diff --git a/src/shaders/SkColorFilterShader.h b/src/shaders/SkColorFilterShader.h
index 2ecdb2d..8cc8a0b 100644
--- a/src/shaders/SkColorFilterShader.h
+++ b/src/shaders/SkColorFilterShader.h
@@ -21,11 +21,17 @@
     std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
 #endif
 
-protected:
+private:
+    bool isOpaque() const override;
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
 
-private:
+    bool onProgram(skvm::Builder*,
+                   SkColorSpace* dstCS,
+                   skvm::Uniforms* uniforms,
+                   skvm::F32 x, skvm::F32 y,
+                   skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
+
     SK_FLATTENABLE_HOOKS(SkColorFilterShader)
 
     sk_sp<SkShader>      fShader;
diff --git a/src/shaders/SkColorShader.cpp b/src/shaders/SkColorShader.cpp
index 5d40c7b..75668a8 100644
--- a/src/shaders/SkColorShader.cpp
+++ b/src/shaders/SkColorShader.cpp
@@ -90,6 +90,38 @@
     return true;
 }
 
+static bool common_program(SkColor4f color, SkColorSpace* cs,
+                           skvm::Builder* p,
+                           SkColorSpace* dstCS,
+                           skvm::Uniforms* uniforms,
+                           skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
+    SkColorSpaceXformSteps(   cs, kUnpremul_SkAlphaType,
+                           dstCS,   kPremul_SkAlphaType).apply(color.vec());
+
+    *r = p->uniformF(uniforms->pushF(color.fR));
+    *g = p->uniformF(uniforms->pushF(color.fG));
+    *b = p->uniformF(uniforms->pushF(color.fB));
+    *a = p->uniformF(uniforms->pushF(color.fA));
+    return true;
+}
+
+bool SkColorShader::onProgram(skvm::Builder* p,
+                              SkColorSpace* dstCS,
+                              skvm::Uniforms* uniforms,
+                              skvm::F32 /*x*/, skvm::F32 /*y*/,
+                              skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    return common_program(SkColor4f::FromColor(fColor), sk_srgb_singleton(),
+                          p, dstCS, uniforms, r,g,b,a);
+}
+bool SkColor4Shader::onProgram(skvm::Builder* p,
+                               SkColorSpace* dstCS,
+                               skvm::Uniforms* uniforms,
+                               skvm::F32 /*x*/, skvm::F32 /*y*/,
+                               skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    return common_program(fColor, fColorSpace.get(),
+                          p, dstCS, uniforms, r,g,b,a);
+}
+
 #if SK_SUPPORT_GPU
 
 #include "src/gpu/GrColorInfo.h"
diff --git a/src/shaders/SkColorShader.h b/src/shaders/SkColorShader.h
index 4e152e3..2d73ad2 100644
--- a/src/shaders/SkColorShader.h
+++ b/src/shaders/SkColorShader.h
@@ -44,6 +44,12 @@
 
     bool onAppendStages(const SkStageRec&) const override;
 
+    bool onProgram(skvm::Builder*,
+                   SkColorSpace* dstCS,
+                   skvm::Uniforms* uniforms,
+                   skvm::F32 x, skvm::F32 y,
+                   skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
+
     SkColor fColor;
 };
 
@@ -64,6 +70,12 @@
     void flatten(SkWriteBuffer&) const override;
     bool onAppendStages(const SkStageRec&) const override;
 
+    bool onProgram(skvm::Builder*,
+                   SkColorSpace* dstCS,
+                   skvm::Uniforms* uniforms,
+                   skvm::F32 x, skvm::F32 y,
+                   skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const override;
+
     sk_sp<SkColorSpace> fColorSpace;
     const SkColor4f     fColor;
 };
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
old mode 100644
new mode 100755
index 601fabd..4cebcb4
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -106,9 +106,11 @@
     if (fImage->colorType() != kN32_SkColorType) {
         return nullptr;
     }
+#if !defined(SK_SUPPORT_LEGACY_TILED_BITMAPS)
     if (fTileModeX != fTileModeY) {
         return nullptr;
     }
+#endif
     if (fTileModeX == SkTileMode::kDecal || fTileModeY == SkTileMode::kDecal) {
         return nullptr;
     }
@@ -233,7 +235,7 @@
         return nullptr;
     }
 
-    GrColorType srcColorType = SkColorTypeToGrColorType(fImage->colorType());
+    SkAlphaType srcAlphaType = fImage->alphaType();
 
     lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
 
@@ -242,21 +244,21 @@
         // domainX and domainY will properly apply the decal effect with the texture domain used in
         // the bicubic filter if clamp to border was unsupported in hardware
         static constexpr auto kDir = GrBicubicEffect::Direction::kXY;
-        inner = GrBicubicEffect::Make(std::move(proxy), srcColorType, lmInverse, wrapModes, domainX,
-                                      domainY, kDir, fImage->alphaType());
+        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY,
+                                      kDir, srcAlphaType);
     } else {
+        auto dimensions = proxy->dimensions();
+        inner = GrSimpleTextureEffect::Make(std::move(proxy), srcAlphaType, lmInverse,
+                                            samplerState);
         if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
-            SkRect domain = GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(proxy->dimensions()),
+            SkRect domain = GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(dimensions),
                                                              domainX, domainY);
-            inner = GrTextureDomainEffect::Make(std::move(proxy), srcColorType, lmInverse, domain,
-                                                domainX, domainY, samplerState);
-        } else {
-            inner = GrSimpleTextureEffect::Make(std::move(proxy), srcColorType, lmInverse,
-                                                samplerState);
+            inner = GrDomainEffect::Make(std::move(inner), domain, domainX, domainY,
+                                         samplerState.filter());
         }
     }
-    inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(),
-                                          fImage->alphaType(), args.fDstColorInfo->colorSpace());
+    inner = GrColorSpaceXformEffect::Make(std::move(inner), fImage->colorSpace(), srcAlphaType,
+                                          args.fDstColorInfo->colorSpace());
 
     bool isAlphaOnly = SkColorTypeIsAlphaOnly(fImage->colorType());
     if (isAlphaOnly) {
diff --git a/src/shaders/SkLights.h b/src/shaders/SkLights.h
index f22d6bb..7e2dedd 100644
--- a/src/shaders/SkLights.h
+++ b/src/shaders/SkLights.h
@@ -20,7 +20,7 @@
     SkLights encapsulates a set of directional, point and ambient lights for use with the
     SkLightingShader.
 */
-class SK_API SkLights  : public SkRefCnt {
+class SkLights  : public SkRefCnt {
 public:
     class Light {
     public:
diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp
index 812dc16..46cf30c 100644
--- a/src/shaders/SkPerlinNoiseShader.cpp
+++ b/src/shaders/SkPerlinNoiseShader.cpp
@@ -738,7 +738,7 @@
     bool stitchTiles() const { return fStitchTiles; }
     const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
     int numOctaves() const { return fNumOctaves; }
-    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
+    const SkMatrix& matrix() const { return fCoordTransform.matrix(); }
 
 private:
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
@@ -1166,7 +1166,7 @@
     const SkVector& baseFrequency() const { return fPaintingData->fBaseFrequency; }
     SkScalar z() const { return fZ; }
     int octaves() const { return fOctaves; }
-    const SkMatrix& matrix() const { return fCoordTransform.getMatrix(); }
+    const SkMatrix& matrix() const { return fCoordTransform.matrix(); }
 
 private:
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
diff --git a/src/shaders/SkRTShader.h b/src/shaders/SkRTShader.h
index 57d69c3..55af34b 100644
--- a/src/shaders/SkRTShader.h
+++ b/src/shaders/SkRTShader.h
@@ -49,7 +49,7 @@
     typedef SkShaderBase INHERITED;
 };
 
-class SkRuntimeShaderFactory {
+class SK_API SkRuntimeShaderFactory {
 public:
     SkRuntimeShaderFactory(SkString sksl, bool isOpaque);
 
diff --git a/src/shaders/SkShader.cpp b/src/shaders/SkShader.cpp
index 0095a95..7455488 100644
--- a/src/shaders/SkShader.cpp
+++ b/src/shaders/SkShader.cpp
@@ -111,7 +111,12 @@
 SkShaderBase::Context::~Context() {}
 
 bool SkShaderBase::ContextRec::isLegacyCompatible(SkColorSpace* shaderColorSpace) const {
-    return !SkColorSpaceXformSteps::Required(shaderColorSpace, fDstColorSpace);
+    // In legacy pipelines, shaders always produce premul (or opaque) and the destination is also
+    // always premul (or opaque).  (And those "or opaque" caveats won't make any difference here.)
+    SkAlphaType shaderAT = kPremul_SkAlphaType,
+                   dstAT = kPremul_SkAlphaType;
+    return 0 == SkColorSpaceXformSteps{shaderColorSpace, shaderAT,
+                                         fDstColorSpace,    dstAT}.flags.mask();
 }
 
 SkImage* SkShader::isAImage(SkMatrix* localMatrix, SkTileMode xy[2]) const {
@@ -192,6 +197,32 @@
     return false;
 }
 
+bool SkShaderBase::program(skvm::Builder* p,
+                           SkColorSpace* dstCS,
+                           skvm::Uniforms* uniforms,
+                           skvm::F32 x, skvm::F32 y,
+                           skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+    // Force opaque alpha for all opaque shaders.
+    //
+    // This is primarily nice in that we usually have a 1.0f constant splat
+    // somewhere in the program anyway, and this will let us drop the work the
+    // shader notionally does to produce alpha, p->extract(...), etc. in favor
+    // of that simple hoistable splat.
+    //
+    // More subtly, it makes isOpaque() a parameter to all shader program
+    // generation, guaranteeing that is-opaque bit is mixed into the overall
+    // shader program hash and blitter Key.  This makes it safe for us to use
+    // that bit to make decisions when constructing an SkVMBlitter, like doing
+    // SrcOver -> Src strength reduction.
+    if (this->onProgram(p, dstCS, uniforms, x,y, r,g,b,a)) {
+        if (this->isOpaque()) {
+            *a = p->splat(1.0f);
+        }
+        return true;
+    }
+    return false;
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 sk_sp<SkFlattenable> SkEmptyShader::CreateProc(SkReadBuffer&) {
diff --git a/src/shaders/SkShaderBase.h b/src/shaders/SkShaderBase.h
index e071e22..4acc1ef 100644
--- a/src/shaders/SkShaderBase.h
+++ b/src/shaders/SkShaderBase.h
@@ -15,6 +15,7 @@
 #include "src/core/SkEffectPriv.h"
 #include "src/core/SkMask.h"
 #include "src/core/SkTLazy.h"
+#include "src/core/SkVM.h"
 
 #if SK_SUPPORT_GPU
 #include "src/gpu/GrFPArgs.h"
@@ -206,6 +207,20 @@
         return this->onAppendUpdatableStages(rec);
     }
 
+    bool program(skvm::Builder*,
+                 SkColorSpace* dstCS,
+                 skvm::Uniforms* uniforms,
+                 skvm::F32 x, skvm::F32 y,
+                 skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const;
+
+    virtual bool onProgram(skvm::Builder*,
+                           SkColorSpace* dstCS,
+                           skvm::Uniforms* uniforms,
+                           skvm::F32 x, skvm::F32 y,
+                           skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) const {
+        return false;
+    }
+
 protected:
     SkShaderBase(const SkMatrix* localMatrix = nullptr);
 
diff --git a/src/sksl/README b/src/sksl/README
index c1d7ae6..906fd81 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -24,7 +24,7 @@
   the same as if and switch in all respects other than it being a compile-time
   error to use a non-constant expression as a test.
 * GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g.
-  sk_Caps.sampleVariablesSupport. The value will be a constant boolean or int,
+  sk_Caps.canUseAnyFunctionInShader. The value will be a constant boolean or int,
   as appropriate. As SkSL supports constant folding and branch elimination, this
   means that an 'if' statement which statically queries a cap will collapse down
   to the chosen branch, meaning that:
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 560a3a7..e7c214b 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -1025,8 +1025,16 @@
 }
 
 void ByteCodeGenerator::writeFunctionCall(const FunctionCall& f) {
-    // Builtins have simple signatures...
-    if (f.fFunction.fBuiltin) {
+    // Find the index of the function we're calling. We explicitly do not allow calls to functions
+    // before they're defined. This is an easy-to-understand rule that prevents recursion.
+    int idx = -1;
+    for (size_t i = 0; i < fFunctions.size(); ++i) {
+        if (f.fFunction.matches(fFunctions[i]->fDeclaration)) {
+            idx = i;
+            break;
+        }
+    }
+    if (idx == -1) {
         for (const auto& arg : f.fArguments) {
             this->writeExpression(*arg);
         }
@@ -1034,18 +1042,11 @@
         return;
     }
 
-    // Find the index of the function we're calling. We explicitly do not allow calls to functions
-    // before they're defined. This is an easy-to-understand rule that prevents recursion.
-    size_t idx;
-    for (idx = 0; idx < fFunctions.size(); ++idx) {
-        if (f.fFunction.matches(fFunctions[idx]->fDeclaration)) {
-            break;
-        }
-    }
+
     if (idx > 255) {
         fErrors.error(f.fOffset, "Function count limit exceeded");
         return;
-    } else if (idx >= fFunctions.size()) {
+    } else if (idx >= (int) fFunctions.size()) {
         fErrors.error(f.fOffset, "Call to undefined function");
         return;
     }
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index a4d2e3b..f77af09 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -122,8 +122,7 @@
             }
             int64_t index = ((IntLiteral&) *i.fIndex).fValue;
             String name = "sk_TransformedCoords2D_" + to_string(index);
-            fFormatArgs.push_back("_outer.computeLocalCoordsInVertexShader() ? " + name +
-                                  ".c_str() : \"_coords\"");
+            fFormatArgs.push_back(name + ".c_str()");
             if (fWrittenTransformedCoords.find(index) == fWrittenTransformedCoords.end()) {
                 addExtraEmitCodeLine("SkString " + name +
                                      " = fragBuilder->ensureCoords2D(args.fTransformedCoords[" +
@@ -498,7 +497,7 @@
         SkASSERT(c.fArguments[0]->fKind == Expression::kVariableReference_Kind);
         String sampler = this->getSamplerHandle(((VariableReference&) *c.fArguments[0]).fVariable);
         fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerSwizzle(" + sampler +
-                              ").c_str()");
+                              ").asString().c_str()");
     }
 }
 
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 7aa6b9f..31492bd 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -28,6 +28,11 @@
 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
 #include "src/sksl/ir/SkSLVarDeclarations.h"
 
+#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
+#include "include/gpu/GrContextOptions.h"
+#include "src/gpu/GrShaderCaps.h"
+#endif
+
 #ifdef SK_ENABLE_SPIRV_VALIDATION
 #include "spirv-tools/libspirv.hpp"
 #endif
@@ -67,6 +72,32 @@
 
 namespace SkSL {
 
+static void grab_intrinsics(std::vector<std::unique_ptr<ProgramElement>>* src,
+               std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>>* target) {
+    for (auto& element : *src) {
+        switch (element->fKind) {
+            case ProgramElement::kFunction_Kind: {
+                FunctionDefinition& f = (FunctionDefinition&) *element;
+                StringFragment name = f.fDeclaration.fName;
+                SkASSERT(target->find(name) == target->end());
+                (*target)[name] = std::make_pair(std::move(element), false);
+                break;
+            }
+            case ProgramElement::kEnum_Kind: {
+                Enum& e = (Enum&) *element;
+                StringFragment name = e.fTypeName;
+                SkASSERT(target->find(name) == target->end());
+                (*target)[name] = std::make_pair(std::move(element), false);
+                break;
+            }
+            default:
+                printf("unsupported include file element\n");
+                SkASSERT(false);
+        }
+    }
+}
+
+
 Compiler::Compiler(Flags flags)
 : fFlags(flags)
 , fContext(new Context())
@@ -223,9 +254,14 @@
                                     *fContext->fSkArgs_Type, Variable::kGlobal_Storage);
     fIRGenerator->fSymbolTable->add(skArgsName, std::unique_ptr<Symbol>(skArgs));
 
-    std::vector<std::unique_ptr<ProgramElement>> ignored;
+    fIRGenerator->fIntrinsics = &fGPUIntrinsics;
+    std::vector<std::unique_ptr<ProgramElement>> gpuIntrinsics;
     this->processIncludeFile(Program::kFragment_Kind, SKSL_GPU_INCLUDE, strlen(SKSL_GPU_INCLUDE),
-                             symbols, &ignored, &fGpuSymbolTable);
+                             symbols, &gpuIntrinsics, &fGpuSymbolTable);
+    grab_intrinsics(&gpuIntrinsics, &fGPUIntrinsics);
+    // need to hang on to the source so that FunctionDefinition.fSource pointers in this file
+    // remain valid
+    fGpuIncludeSource = std::move(fIRGenerator->fFile);
     this->processIncludeFile(Program::kVertex_Kind, SKSL_VERT_INCLUDE, strlen(SKSL_VERT_INCLUDE),
                              fGpuSymbolTable, &fVertexInclude, &fVertexSymbolTable);
     this->processIncludeFile(Program::kFragment_Kind, SKSL_FRAG_INCLUDE, strlen(SKSL_FRAG_INCLUDE),
@@ -235,9 +271,11 @@
     this->processIncludeFile(Program::kPipelineStage_Kind, SKSL_PIPELINE_INCLUDE,
                              strlen(SKSL_PIPELINE_INCLUDE), fGpuSymbolTable, &fPipelineInclude,
                              &fPipelineSymbolTable);
+    std::vector<std::unique_ptr<ProgramElement>> interpIntrinsics;
     this->processIncludeFile(Program::kGeneric_Kind, SKSL_INTERP_INCLUDE,
                              strlen(SKSL_INTERP_INCLUDE), symbols, &fInterpreterInclude,
                              &fInterpreterSymbolTable);
+    grab_intrinsics(&interpIntrinsics, &fInterpreterIntrinsics);
 }
 
 Compiler::~Compiler() {
@@ -248,8 +286,17 @@
                                   std::shared_ptr<SymbolTable> base,
                                   std::vector<std::unique_ptr<ProgramElement>>* outElements,
                                   std::shared_ptr<SymbolTable>* outSymbolTable) {
+#ifdef SK_DEBUG
+    String source(src, length);
+    fSource = &source;
+#endif
     fIRGenerator->fSymbolTable = std::move(base);
     Program::Settings settings;
+#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
+    GrContextOptions opts;
+    GrShaderCaps caps(opts);
+    settings.fCaps = &caps;
+#endif
     fIRGenerator->start(&settings, nullptr);
     fIRGenerator->convertProgram(kind, src, length, *fTypes, outElements);
     if (this->fErrorCount) {
@@ -258,6 +305,9 @@
     SkASSERT(!fErrorCount);
     fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
     *outSymbolTable = fIRGenerator->fSymbolTable;
+#ifdef SK_DEBUG
+    fSource = nullptr;
+#endif
 }
 
 // add the definition created by assigning to the lvalue to the definition set
@@ -1290,7 +1340,8 @@
     // check for missing return
     if (f.fDeclaration.fReturnType != *fContext->fVoid_Type) {
         if (cfg.fBlocks[cfg.fExit].fEntrances.size()) {
-            this->error(f.fOffset, String("function can exit without returning a value"));
+            this->error(f.fOffset, String("function '" + String(f.fDeclaration.fName) +
+                                          "' can exit without returning a value"));
         }
     }
 }
@@ -1313,22 +1364,26 @@
         case Program::kVertex_Kind:
             inherited = &fVertexInclude;
             fIRGenerator->fSymbolTable = fVertexSymbolTable;
+            fIRGenerator->fIntrinsics = &fGPUIntrinsics;
             fIRGenerator->start(&settings, inherited);
             break;
         case Program::kFragment_Kind:
             inherited = &fFragmentInclude;
             fIRGenerator->fSymbolTable = fFragmentSymbolTable;
+            fIRGenerator->fIntrinsics = &fGPUIntrinsics;
             fIRGenerator->start(&settings, inherited);
             break;
         case Program::kGeometry_Kind:
             inherited = &fGeometryInclude;
             fIRGenerator->fSymbolTable = fGeometrySymbolTable;
+            fIRGenerator->fIntrinsics = &fGPUIntrinsics;
             fIRGenerator->start(&settings, inherited);
             break;
         case Program::kFragmentProcessor_Kind:
             inherited = nullptr;
             fIRGenerator->fSymbolTable = fGpuSymbolTable;
             fIRGenerator->start(&settings, nullptr);
+            fIRGenerator->fIntrinsics = &fGPUIntrinsics;
             fIRGenerator->convertProgram(kind, SKSL_FP_INCLUDE, strlen(SKSL_FP_INCLUDE), *fTypes,
                                          &elements);
             fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
@@ -1336,11 +1391,13 @@
         case Program::kPipelineStage_Kind:
             inherited = &fPipelineInclude;
             fIRGenerator->fSymbolTable = fPipelineSymbolTable;
+            fIRGenerator->fIntrinsics = &fGPUIntrinsics;
             fIRGenerator->start(&settings, inherited);
             break;
         case Program::kGeneric_Kind:
             inherited = &fInterpreterInclude;
             fIRGenerator->fSymbolTable = fInterpreterSymbolTable;
+            fIRGenerator->fIntrinsics = &fInterpreterIntrinsics;
             fIRGenerator->start(&settings, inherited);
             break;
     }
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index 7ef368d..a4b564e 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -8,9 +8,11 @@
 #ifndef SKSL_COMPILER
 #define SKSL_COMPILER
 
+#include <map>
 #include <set>
 #include <unordered_set>
 #include <vector>
+#include "src/sksl/SkSLASTFile.h"
 #include "src/sksl/SkSLCFGGenerator.h"
 #include "src/sksl/SkSLContext.h"
 #include "src/sksl/SkSLErrorReporter.h"
@@ -36,6 +38,7 @@
 #define SK_HEIGHT_BUILTIN              10012
 #define SK_FRAGCOORD_BUILTIN              15
 #define SK_CLOCKWISE_BUILTIN              17
+#define SK_SAMPLEMASK_BUILTIN             20
 #define SK_VERTEXID_BUILTIN               42
 #define SK_INSTANCEID_BUILTIN             43
 #define SK_CLIPDISTANCE_BUILTIN            3
@@ -101,6 +104,7 @@
         SkString fName;
         std::vector<GrShaderVar> fParameters;
         SkString fBody;
+        std::vector<Compiler::FormatArg> fFormatArgs;
     };
 #endif
 
@@ -212,6 +216,9 @@
 
     Position position(int offset);
 
+    std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>> fGPUIntrinsics;
+    std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>> fInterpreterIntrinsics;
+    std::unique_ptr<ASTFile> fGpuIncludeSource;
     std::shared_ptr<SymbolTable> fGpuSymbolTable;
     std::vector<std::unique_ptr<ProgramElement>> fVertexInclude;
     std::shared_ptr<SymbolTable> fVertexSymbolTable;
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index df7c0b0..c555b8a 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -153,6 +153,8 @@
             }
             break;
         }
+        case Type::kEnum_Kind:
+            return "int";
         default:
             return type.name();
     }
@@ -796,6 +798,10 @@
         case SK_CLOCKWISE_BUILTIN:
             this->write(fProgram.fSettings.fFlipY ? "(!gl_FrontFacing)" : "gl_FrontFacing");
             break;
+        case SK_SAMPLEMASK_BUILTIN:
+            SkASSERT(fProgram.fSettings.fCaps->sampleMaskSupport());
+            this->write("gl_SampleMask");
+            break;
         case SK_VERTEXID_BUILTIN:
             this->write("gl_VertexID");
             break;
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index 31cc890..0790a1e 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -66,7 +66,7 @@
     } else if (type == *context.fFloat4x4_Type || type == *context.fHalf4x4_Type) {
         return Layout::CType::kSkMatrix44;
     } else if (type.kind() == Type::kSampler_Kind) {
-        return Layout::CType::kGrTextureProxy;
+        return Layout::CType::kGrSurfaceProxy;
     } else if (type == *context.fFragmentProcessor_Type) {
         return Layout::CType::kGrFragmentProcessor;
     }
@@ -282,7 +282,7 @@
             this->writef("            %s_index = this->numChildProcessors();",
                          FieldName(String(param->fName).c_str()).c_str());
             if (fSectionAndParameterHelper.hasCoordOverrides(*param)) {
-                this->writef("            %s->setComputeLocalCoordsInVertexShader(false);",
+                this->writef("            %s->setSampledWithExplicitCoords(true);",
                              String(param->fName).c_str());
             }
             this->writef("            this->registerChildProcessor(std::move(%s));",
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index 9b71c17..5157235 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -36,6 +36,7 @@
 #include "src/sksl/ir/SkSLIntLiteral.h"
 #include "src/sksl/ir/SkSLInterfaceBlock.h"
 #include "src/sksl/ir/SkSLLayout.h"
+#include "src/sksl/ir/SkSLNop.h"
 #include "src/sksl/ir/SkSLNullLiteral.h"
 #include "src/sksl/ir/SkSLPostfixExpression.h"
 #include "src/sksl/ir/SkSLPrefixExpression.h"
@@ -126,7 +127,6 @@
     CAP(fbFetchNeedsCustomOutput);
     CAP(flatInterpolationSupport);
     CAP(noperspectiveInterpolationSupport);
-    CAP(sampleVariablesSupport);
     CAP(externalTextureSupport);
     CAP(mustEnableAdvBlendEqs);
     CAP(mustEnableSpecificAdvBlendEqs);
@@ -169,6 +169,10 @@
             }
         }
     }
+    SkASSERT(fIntrinsics);
+    for (auto& pair : *fIntrinsics) {
+        pair.second.second = false;
+    }
 }
 
 std::unique_ptr<Extension> IRGenerator::convertExtension(int offset, StringFragment name) {
@@ -833,7 +837,7 @@
                             return;
                         }
                     }
-                    if (other->fDefined) {
+                    if (other->fDefined && !other->fBuiltin) {
                         fErrors.error(f.fOffset, "duplicate definition of " +
                                                  other->description());
                     }
@@ -893,8 +897,10 @@
         if (Program::kVertex_Kind == fKind && fd.fName == "main" && fRTAdjust) {
             body->fStatements.insert(body->fStatements.end(), this->getNormalizeSkPositionCode());
         }
-        fProgramElements->push_back(std::unique_ptr<FunctionDefinition>(
-                                        new FunctionDefinition(f.fOffset, *decl, std::move(body))));
+        std::unique_ptr<FunctionDefinition> result(new FunctionDefinition(f.fOffset, *decl,
+                                                                          std::move(body)));
+        result->fSource = &f;
+        fProgramElements->push_back(std::move(result));
     }
 }
 
@@ -1748,6 +1754,16 @@
 std::unique_ptr<Expression> IRGenerator::call(int offset,
                                               const FunctionDeclaration& function,
                                               std::vector<std::unique_ptr<Expression>> arguments) {
+    if (function.fBuiltin) {
+        auto found = fIntrinsics->find(function.fName);
+        if (found != fIntrinsics->end() && !found->second.second) {
+            found->second.second = true;
+            const FunctionDeclaration* old = fCurrentFunction;
+            fCurrentFunction = nullptr;
+            this->convertFunction(*((FunctionDefinition&) *found->second.first).fSource);
+            fCurrentFunction = old;
+        }
+    }
     if (function.fParameters.size() != arguments.size()) {
         String msg = "call to '" + function.fName + "' expected " +
                                  to_string((uint64_t) function.fParameters.size()) +
@@ -2246,10 +2262,23 @@
             fSymbolTable = ((Enum&) *e).fSymbols;
             result = convertIdentifier(ASTNode(&fFile->fNodes, offset, ASTNode::Kind::kIdentifier,
                                                field));
+            SkASSERT(result->fKind == Expression::kVariableReference_Kind);
+            const Variable& v = ((VariableReference&) *result).fVariable;
+            SkASSERT(v.fInitialValue);
+            SkASSERT(v.fInitialValue->fKind == Expression::kIntLiteral_Kind);
+            result.reset(new IntLiteral(offset, ((IntLiteral&) *v.fInitialValue).fValue, &type));
             fSymbolTable = old;
+            break;
         }
     }
     if (!result) {
+        auto found = fIntrinsics->find(type.fName);
+        if (found != fIntrinsics->end()) {
+            SkASSERT(!found->second.second);
+            found->second.second = true;
+            fProgramElements->push_back(found->second.first->clone());
+            return this->convertTypeField(offset, type, field);
+        }
         fErrors.error(offset, "type '" + type.fName + "' does not have a field named '" + field +
                               "'");
     }
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 20a556f..a088444 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -8,6 +8,8 @@
 #ifndef SKSL_IRGENERATOR
 #define SKSL_IRGENERATOR
 
+#include <map>
+
 #include "src/sksl/SkSLASTFile.h"
 #include "src/sksl/SkSLASTNode.h"
 #include "src/sksl/SkSLErrorReporter.h"
@@ -143,6 +145,7 @@
     std::unique_ptr<Block> applyInvocationIDWorkaround(std::unique_ptr<Block> main);
     // returns a statement which converts sk_Position from device to normalized coordinates
     std::unique_ptr<Statement> getNormalizeSkPositionCode();
+    void removeSampleMask(std::vector<std::unique_ptr<ProgramElement>>* out);
 
     void checkValid(const Expression& expr);
     void setRefKind(const Expression& expr, VariableReference::RefKind kind);
@@ -154,6 +157,9 @@
     std::unordered_map<String, Program::Settings::Value> fCapsMap;
     std::shared_ptr<SymbolTable> fRootSymbolTable;
     std::shared_ptr<SymbolTable> fSymbolTable;
+    // Symbols which have definitions in the include files. The bool tells us whether this
+    // intrinsic has been included already.
+    std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>>* fIntrinsics = nullptr;
     // holds extra temp variable declarations needed for the current function
     std::vector<std::unique_ptr<Statement>> fExtraVars;
     int fLoopLevel;
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index dde55f6..aae6263 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -215,6 +215,10 @@
         return GrSLType::kFloat2_GrSLType;
     } else if (type == *context.fHalf2_Type) {
         return GrSLType::kHalf2_GrSLType;
+    } else if (type == *context.fFloat3_Type) {
+        return GrSLType::kFloat3_GrSLType;
+    } else if (type == *context.fHalf3_Type) {
+        return GrSLType::kHalf3_GrSLType;
     } else if (type == *context.fFloat4_Type) {
         return GrSLType::kFloat4_GrSLType;
     } else if (type == *context.fHalf4_Type) {
@@ -262,6 +266,7 @@
         }
         fOut = oldOut;
         result.fBody = buffer.str();
+        result.fFormatArgs = std::move(*fFormatArgs);
         fFunctions->push_back(result);
     }
 }
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index b7ffd1c..858d8ca 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2713,6 +2713,22 @@
     }
 }
 
+bool is_dead(const Variable& var) {
+    if (var.fReadCount || var.fWriteCount) {
+        return false;
+    }
+    // not entirely sure what the rules are for when it's safe to elide interface variables, but it
+    // causes various problems to elide some of them even when dead. But it also causes problems
+    // *not* to elide sk_SampleMask when it's not being used.
+    if (!(var.fModifiers.fFlags & (Modifiers::kIn_Flag |
+                                   Modifiers::kOut_Flag |
+                                   Modifiers::kUniform_Flag |
+                                   Modifiers::kBuffer_Flag))) {
+        return true;
+    }
+    return var.fModifiers.fLayout.fBuiltin == SK_SAMPLEMASK_BUILTIN;
+}
+
 #define BUILTIN_IGNORE 9999
 void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl,
                                          OutputStream& out) {
@@ -2725,10 +2741,10 @@
         // These haven't been implemented in our SPIR-V generator yet and we only currently use them
         // in the OpenGL backend.
         SkASSERT(!(var->fModifiers.fFlags & (Modifiers::kReadOnly_Flag |
-                                           Modifiers::kWriteOnly_Flag |
-                                           Modifiers::kCoherent_Flag |
-                                           Modifiers::kVolatile_Flag |
-                                           Modifiers::kRestrict_Flag)));
+                                             Modifiers::kWriteOnly_Flag |
+                                             Modifiers::kCoherent_Flag |
+                                             Modifiers::kVolatile_Flag |
+                                             Modifiers::kRestrict_Flag)));
         if (var->fModifiers.fLayout.fBuiltin == BUILTIN_IGNORE) {
             continue;
         }
@@ -2737,13 +2753,7 @@
             SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
             continue;
         }
-        if (!var->fReadCount && !var->fWriteCount &&
-                !(var->fModifiers.fFlags & (Modifiers::kIn_Flag |
-                                            Modifiers::kOut_Flag |
-                                            Modifiers::kUniform_Flag |
-                                            Modifiers::kBuffer_Flag))) {
-            // variable is dead and not an input / output var (the Vulkan debug layers complain if
-            // we elide an interface var, even if it's dead)
+        if (is_dead(*var)) {
             continue;
         }
         SpvStorageClass_ storageClass;
@@ -3161,7 +3171,8 @@
             SpvId id = this->writeInterfaceBlock(intf);
             if (((intf.fVariable.fModifiers.fFlags & Modifiers::kIn_Flag) ||
                 (intf.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag)) &&
-                intf.fVariable.fModifiers.fLayout.fBuiltin == -1) {
+                intf.fVariable.fModifiers.fLayout.fBuiltin == -1 &&
+                !is_dead(intf.fVariable)) {
                 interfaceVars.insert(id);
             }
         }
@@ -3190,7 +3201,7 @@
         const Variable* var = entry.first;
         if (var->fStorage == Variable::kGlobal_Storage &&
             ((var->fModifiers.fFlags & Modifiers::kIn_Flag) ||
-             (var->fModifiers.fFlags & Modifiers::kOut_Flag))) {
+             (var->fModifiers.fFlags & Modifiers::kOut_Flag)) && !is_dead(*var)) {
             interfaceVars.insert(entry.second);
         }
     }
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index 60057b6..be9a74e 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -96,7 +96,7 @@
         return true;
     }
 
-    bool sampleVariablesSupport() const {
+    bool sampleMaskSupport() const {
         return true;
     }
 
@@ -377,6 +377,12 @@
         result->fRemovePowWithConstantExponent = true;
         return result;
     }
+
+    static sk_sp<GrShaderCaps> SampleMaskSupport() {
+        sk_sp<GrShaderCaps> result = Default();
+        result->fSampleMaskSupport = true;
+        return result;
+    }
 };
 #endif
 
diff --git a/src/sksl/ir/SkSLFunctionDefinition.h b/src/sksl/ir/SkSLFunctionDefinition.h
index 7344373..511a0f8 100644
--- a/src/sksl/ir/SkSLFunctionDefinition.h
+++ b/src/sksl/ir/SkSLFunctionDefinition.h
@@ -14,6 +14,8 @@
 
 namespace SkSL {
 
+struct ASTNode;
+
 /**
  * A function definition (a declaration plus an associated block of code).
  */
@@ -35,6 +37,11 @@
 
     const FunctionDeclaration& fDeclaration;
     std::unique_ptr<Statement> fBody;
+    // This pointer may be null, and even when non-null is not guaranteed to remain valid for the
+    // entire lifespan of this object. The parse tree's lifespan is normally controlled by
+    // IRGenerator, so the IRGenerator being destroyed or being used to compile another file will
+    // invalidate this pointer.
+    const ASTNode* fSource = nullptr;
 
     typedef ProgramElement INHERITED;
 };
diff --git a/src/sksl/ir/SkSLIntLiteral.h b/src/sksl/ir/SkSLIntLiteral.h
index a95875c..fa45978 100644
--- a/src/sksl/ir/SkSLIntLiteral.h
+++ b/src/sksl/ir/SkSLIntLiteral.h
@@ -45,7 +45,8 @@
     }
 
     int coercionCost(const Type& target) const override {
-        if (target.isSigned() || target.isUnsigned() || target.isFloat()) {
+        if (target.isSigned() || target.isUnsigned() || target.isFloat() ||
+            target.kind() == Type::kEnum_Kind) {
             return 0;
         }
         return INHERITED::coercionCost(target);
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index 143aff3..24c7431 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -82,6 +82,9 @@
         kDefault,
         kBool,
         kFloat,
+        kFloat2,
+        kFloat3,
+        kFloat4,
         kInt32,
         kSkRect,
         kSkIRect,
@@ -92,7 +95,7 @@
         kSkIPoint,
         kSkMatrix,
         kSkMatrix44,
-        kGrTextureProxy,
+        kGrSurfaceProxy,
         kGrFragmentProcessor,
     };
 
@@ -174,8 +177,8 @@
                 return "SkMatrix";
             case CType::kSkMatrix44:
                 return "SkMatrix44";
-            case CType::kGrTextureProxy:
-                return "sk_sp<GrTextureProxy>";
+            case CType::kGrSurfaceProxy:
+                return "sk_sp<GrSurfaceProxy>";
             case CType::kGrFragmentProcessor:
                 return "std::unique_ptr<GrFragmentProcessor>";
             default:
diff --git a/src/sksl/ir/SkSLSymbolTable.cpp b/src/sksl/ir/SkSLSymbolTable.cpp
index 08bd6c2..ed2cb4d 100644
--- a/src/sksl/ir/SkSLSymbolTable.cpp
+++ b/src/sksl/ir/SkSLSymbolTable.cpp
@@ -110,9 +110,7 @@
     for (const auto& pair : fSymbols) {
         switch (pair.second->fKind) {
             case Symbol::kFunctionDeclaration_Kind:
-                if (!((FunctionDeclaration&)*pair.second).fDefined) {
-                    ((FunctionDeclaration&)*pair.second).fBuiltin = true;
-                }
+                ((FunctionDeclaration&)*pair.second).fBuiltin = true;
                 break;
             case Symbol::kUnresolvedFunction_Kind:
                 for (auto& f : ((UnresolvedFunction&) *pair.second).fFunctions) {
diff --git a/src/sksl/sksl_fp.inc b/src/sksl/sksl_fp.inc
index 0608230..645ec76 100644
--- a/src/sksl/sksl_fp.inc
+++ b/src/sksl/sksl_fp.inc
@@ -4,6 +4,7 @@
 
 layout(builtin=15) in float4 sk_FragCoord;
 layout(builtin=3) float sk_ClipDistance[1];
+layout(builtin=20) out int sk_SampleMask[1];
 
 // 9999 is a temporary value that causes us to ignore these declarations beyond
 // adding them to the symbol table. This works fine in GLSL (where they do not
@@ -12,8 +13,6 @@
 layout(builtin=9999) float4 gl_LastFragData[1];
 layout(builtin=9999) half4 gl_LastFragColor;
 layout(builtin=9999) half4 gl_LastFragColorARM;
-layout(builtin=9999) int gl_SampleMaskIn[1];
-layout(builtin=9999) out int gl_SampleMask[1];
 layout(builtin=9999) half4 gl_SecondaryFragColorEXT;
 
 layout(builtin=10003) half4 sk_InColor;
diff --git a/src/sksl/sksl_frag.inc b/src/sksl/sksl_frag.inc
index 5bc5f55..72b4177 100644
--- a/src/sksl/sksl_frag.inc
+++ b/src/sksl/sksl_frag.inc
@@ -6,13 +6,12 @@
 layout(builtin=15) in float4 sk_FragCoord;
 layout(builtin=17) in bool sk_Clockwise;  // Similar to gl_FrontFacing, but defined in device space.
 layout(builtin=3) float sk_ClipDistance[1];
+layout(builtin=20) out int sk_SampleMask[1];
 
 // 9999 is a temporary value that causes us to ignore these declarations beyond
 // adding them to the symbol table. This works fine in GLSL (where they do not
 // require any further handling) but will fail in SPIR-V. We'll have a better
 // solution for this soon.
-layout(builtin=9999) int gl_SampleMaskIn[1];
-layout(builtin=9999) out int gl_SampleMask[1];
 layout(builtin=9999) out half4 gl_SecondaryFragColorEXT;
 
 layout(location=0,index=0,builtin=10001) out half4 sk_FragColor;
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 7ee26f7..cb84960 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -728,7 +728,7 @@
             const auto& p = e->getDeviceSpacePath();
             AutoElement path("path", fWriter);
             path.addPathAttributes(p);
-            if (p.getFillType() == SkPath::kEvenOdd_FillType) {
+            if (p.getNewFillType() == SkPathFillType::kEvenOdd) {
                 path.addAttribute("clip-rule", "evenodd");
             }
         } break;
@@ -852,7 +852,7 @@
     elem.addPathAttributes(path);
 
     // TODO: inverse fill types?
-    if (path.getFillType() == SkPath::kEvenOdd_FillType) {
+    if (path.getNewFillType() == SkPathFillType::kEvenOdd) {
         elem.addAttribute("fill-rule", "evenodd");
     }
 }
diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp
index 92e4bdb..f94df30 100644
--- a/src/utils/SkDashPath.cpp
+++ b/src/utils/SkDashPath.cpp
@@ -425,7 +425,7 @@
     } while (meas.nextContour());
 
     if (segCount > 1) {
-        dst->setConvexity(SkPath::kConcave_Convexity);
+        dst->setConvexityType(SkPathConvexityType::kConcave);
     }
 
     return true;
diff --git a/src/utils/SkLua.cpp b/src/utils/SkLua.cpp
index 03318bb..c634d5b 100644
--- a/src/utils/SkLua.cpp
+++ b/src/utils/SkLua.cpp
@@ -1248,22 +1248,22 @@
     return 1;
 }
 
-static const char* fill_type_to_str(SkPath::FillType fill) {
+static const char* fill_type_to_str(SkPathFillType fill) {
     switch (fill) {
-        case SkPath::kEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
             return "even-odd";
-        case SkPath::kWinding_FillType:
+        case SkPathFillType::kWinding:
             return "winding";
-        case SkPath::kInverseEvenOdd_FillType:
+        case SkPathFillType::kInverseEvenOdd:
             return "inverse-even-odd";
-        case SkPath::kInverseWinding_FillType:
+        case SkPathFillType::kInverseWinding:
             return "inverse-winding";
     }
     return "unknown";
 }
 
 static int lpath_getFillType(lua_State* L) {
-    SkPath::FillType fill = get_obj<SkPath>(L, 1)->getFillType();
+    SkPathFillType fill = get_obj<SkPath>(L, 1)->getNewFillType();
     SkLua(L).pushString(fill_type_to_str(fill));
     return 1;
 }
@@ -1310,7 +1310,7 @@
 }
 
 static int lpath_isConvex(lua_State* L) {
-    bool isConvex = SkPath::kConvex_Convexity == get_obj<SkPath>(L, 1)->getConvexity();
+    bool isConvex = get_obj<SkPath>(L, 1)->isConvex();
     SkLua(L).pushBool(isConvex);
     return 1;
 }
diff --git a/src/utils/SkMultiPictureDocument.h b/src/utils/SkMultiPictureDocument.h
index b6d7be8..3c4924b 100644
--- a/src/utils/SkMultiPictureDocument.h
+++ b/src/utils/SkMultiPictureDocument.h
@@ -19,7 +19,7 @@
 /**
  *  Writes into a file format that is similar to SkPicture::serialize()
  */
-SK_API sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* dst, const SkSerialProcs* = nullptr);
+SK_SPI sk_sp<SkDocument> SkMakeMultiPictureDocument(SkWStream* dst, const SkSerialProcs* = nullptr);
 
 struct SkDocumentPage {
     sk_sp<SkPicture> fPicture;
@@ -29,14 +29,14 @@
 /**
  *  Returns the number of pages in the SkMultiPictureDocument.
  */
-SK_API int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* src);
+SK_SPI int SkMultiPictureDocumentReadPageCount(SkStreamSeekable* src);
 
 /**
  *  Read the SkMultiPictureDocument into the provided array of pages.
  *  dstArrayCount must equal SkMultiPictureDocumentReadPageCount().
  *  Return false on error.
  */
-SK_API bool SkMultiPictureDocumentRead(SkStreamSeekable* src,
+SK_SPI bool SkMultiPictureDocumentRead(SkStreamSeekable* src,
                                        SkDocumentPage* dstArray,
                                        int dstArrayCount,
                                        const SkDeserialProcs* = nullptr);
diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
index 8c9469b..688daae 100644
--- a/src/utils/SkParsePath.cpp
+++ b/src/utils/SkParsePath.cpp
@@ -175,7 +175,7 @@
                         && (data = skip_sep(data))
                         && (data = find_points(data, &points[0], 1, relative, &c))) {
                     path.arcTo(radii, angle, (SkPath::ArcSize) SkToBool(largeArc),
-                            (SkPath::Direction) !SkToBool(sweep), points[0]);
+                            (SkPathDirection) !SkToBool(sweep), points[0]);
                     path.getLastPt(&c);
                 }
                 } break;
diff --git a/src/utils/SkPatchUtils.h b/src/utils/SkPatchUtils.h
index 41b4912..dc2870a 100644
--- a/src/utils/SkPatchUtils.h
+++ b/src/utils/SkPatchUtils.h
@@ -14,7 +14,7 @@
 
 class SkColorSpace;
 
-class SK_API SkPatchUtils {
+class SkPatchUtils {
 
 public:
     // Enums for control points based on the order specified in the constructor (clockwise).
diff --git a/src/utils/SkUTF.h b/src/utils/SkUTF.h
index 385102a..676ce4a 100644
--- a/src/utils/SkUTF.h
+++ b/src/utils/SkUTF.h
@@ -3,6 +3,7 @@
 #ifndef SkUTF_DEFINED
 #define SkUTF_DEFINED
 
+#include "include/core/SkTypes.h"
 #include <cstddef>
 #include <cstdint>
 
@@ -13,39 +14,39 @@
 /** Given a sequence of UTF-8 bytes, return the number of unicode codepoints.
     If the sequence is invalid UTF-8, return -1.
 */
-int CountUTF8(const char* utf8, size_t byteLength);
+SK_SPI int CountUTF8(const char* utf8, size_t byteLength);
 
 /** Given a sequence of aligned UTF-16 characters in machine-endian form,
     return the number of unicode codepoints.  If the sequence is invalid
     UTF-16, return -1.
 */
-int CountUTF16(const uint16_t* utf16, size_t byteLength);
+SK_SPI int CountUTF16(const uint16_t* utf16, size_t byteLength);
 
 /** Given a sequence of aligned UTF-32 characters in machine-endian form,
     return the number of unicode codepoints.  If the sequence is invalid
     UTF-32, return -1.
 */
-int CountUTF32(const int32_t* utf32, size_t byteLength);
+SK_SPI int CountUTF32(const int32_t* utf32, size_t byteLength);
 
 /** Given a sequence of UTF-8 bytes, return the first unicode codepoint.
     The pointer will be incremented to point at the next codepoint's start.  If
     invalid UTF-8 is encountered, set *ptr to end and return -1.
 */
-SkUnichar NextUTF8(const char** ptr, const char* end);
+SK_SPI SkUnichar NextUTF8(const char** ptr, const char* end);
 
 /** Given a sequence of aligned UTF-16 characters in machine-endian form,
     return the first unicode codepoint.  The pointer will be incremented to
     point at the next codepoint's start.  If invalid UTF-16 is encountered,
     set *ptr to end and return -1.
 */
-SkUnichar NextUTF16(const uint16_t** ptr, const uint16_t* end);
+SK_SPI SkUnichar NextUTF16(const uint16_t** ptr, const uint16_t* end);
 
 /** Given a sequence of aligned UTF-32 characters in machine-endian form,
     return the first unicode codepoint.  The pointer will be incremented to
     point at the next codepoint's start.  If invalid UTF-32 is encountered,
     set *ptr to end and return -1.
 */
-SkUnichar NextUTF32(const int32_t** ptr, const int32_t* end);
+SK_SPI SkUnichar NextUTF32(const int32_t** ptr, const int32_t* end);
 
 constexpr unsigned kMaxBytesInUTF8Sequence = 4;
 
@@ -54,14 +55,14 @@
     is null, simply return the number of bytes that would be used.  For invalid
     unicode codepoints, return 0.
 */
-size_t ToUTF8(SkUnichar uni, char utf8[kMaxBytesInUTF8Sequence] = nullptr);
+SK_SPI size_t ToUTF8(SkUnichar uni, char utf8[kMaxBytesInUTF8Sequence] = nullptr);
 
 /** Convert the unicode codepoint into UTF-16.  If `utf16` is non-null, place
     the result in that array.  Return the number of UTF-16 code units in the
     result (1 or 2).  If `utf16` is null, simply return the number of code
     units that would be used.  For invalid unicode codepoints, return 0.
 */
-size_t ToUTF16(SkUnichar uni, uint16_t utf16[2] = nullptr);
+SK_SPI size_t ToUTF16(SkUnichar uni, uint16_t utf16[2] = nullptr);
 
 }  // namespace SkUTF
 
diff --git a/src/utils/mac/SkStream_mac.cpp b/src/utils/mac/SkStream_mac.cpp
index 30f8e1e..b299fce 100644
--- a/src/utils/mac/SkStream_mac.cpp
+++ b/src/utils/mac/SkStream_mac.cpp
@@ -5,79 +5,4 @@
  * found in the LICENSE file.
  */
 
-#include "include/core/SkTypes.h"
-
-#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
-
-#include "include/core/SkStream.h"
-#include "include/private/SkMalloc.h"
-#include "include/utils/mac/SkCGUtils.h"
-
-// These are used by CGDataProviderCreateWithData
-
-static void unref_proc(void* info, const void* addr, size_t size) {
-    SkASSERT(info);
-    ((SkRefCnt*)info)->unref();
-}
-
-static void delete_stream_proc(void* info, const void* addr, size_t size) {
-    SkASSERT(info);
-    SkStream* stream = (SkStream*)info;
-    SkASSERT(stream->getMemoryBase() == addr);
-    SkASSERT(stream->getLength() == size);
-    delete stream;
-}
-
-// These are used by CGDataProviderSequentialCallbacks
-
-static size_t get_bytes_proc(void* info, void* buffer, size_t bytes) {
-    SkASSERT(info);
-    return ((SkStream*)info)->read(buffer, bytes);
-}
-
-static off_t skip_forward_proc(void* info, off_t bytes) {
-    return ((SkStream*)info)->skip((size_t) bytes);
-}
-
-static void rewind_proc(void* info) {
-    SkASSERT(info);
-    ((SkStream*)info)->rewind();
-}
-
-// Used when info is an SkStream.
-static void release_info_proc(void* info) {
-    SkASSERT(info);
-    delete (SkStream*)info;
-}
-
-CGDataProviderRef SkCreateDataProviderFromStream(std::unique_ptr<SkStreamRewindable> stream) {
-    // TODO: Replace with SkStream::getData() when that is added. Then we only
-    // have one version of CGDataProviderCreateWithData (i.e. same release proc)
-    const void* addr = stream->getMemoryBase();
-    if (addr) {
-        // special-case when the stream is just a block of ram
-        size_t size = stream->getLength();
-        return CGDataProviderCreateWithData(stream.release(), addr, size, delete_stream_proc);
-    }
-
-    CGDataProviderSequentialCallbacks rec;
-    sk_bzero(&rec, sizeof(rec));
-    rec.version = 0;
-    rec.getBytes = get_bytes_proc;
-    rec.skipForward = skip_forward_proc;
-    rec.rewind = rewind_proc;
-    rec.releaseInfo = release_info_proc;
-    return CGDataProviderCreateSequential(stream.release(), &rec);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "include/core/SkData.h"
-
-CGDataProviderRef SkCreateDataProviderFromData(sk_sp<SkData> data) {
-    const void* addr = data->data();
-    size_t size = data->size();
-    return CGDataProviderCreateWithData(data.release(), addr, size, unref_proc);
-}
-
-#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+// Remove file after it is removed from downstream builds.
diff --git a/src/utils/win/SkDWriteGeometrySink.cpp b/src/utils/win/SkDWriteGeometrySink.cpp
index 65da6ae..3979a13 100644
--- a/src/utils/win/SkDWriteGeometrySink.cpp
+++ b/src/utils/win/SkDWriteGeometrySink.cpp
@@ -49,10 +49,10 @@
 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
     switch (fillMode) {
     case D2D1_FILL_MODE_ALTERNATE:
-        fPath->setFillType(SkPath::kEvenOdd_FillType);
+        fPath->setFillType(SkPathFillType::kEvenOdd);
         break;
     case D2D1_FILL_MODE_WINDING:
-        fPath->setFillType(SkPath::kWinding_FillType);
+        fPath->setFillType(SkPathFillType::kWinding);
         break;
     default:
         SkDEBUGFAIL("Unknown D2D1_FILL_MODE.");
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index 7a4ff99..b789651 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -1182,7 +1182,7 @@
     if (rect_must_be_pathed(paint, this->localToDevice())) {
         SkPath tmp;
         tmp.addRect(r);
-        tmp.setFillType(SkPath::kWinding_FillType);
+        tmp.setFillType(SkPathFillType::kWinding);
         this->drawPath(tmp, paint, true);
         return;
     }
@@ -1607,14 +1607,14 @@
     //Set the fill rule.
     SkPath* xpsCompatiblePath = fillablePath;
     XPS_FILL_RULE xpsFillRule;
-    switch (fillablePath->getFillType()) {
-        case SkPath::kWinding_FillType:
+    switch (fillablePath->getNewFillType()) {
+        case SkPathFillType::kWinding:
             xpsFillRule = XPS_FILL_RULE_NONZERO;
             break;
-        case SkPath::kEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
             xpsFillRule = XPS_FILL_RULE_EVENODD;
             break;
-        case SkPath::kInverseWinding_FillType: {
+        case SkPathFillType::kInverseWinding: {
             //[Fillable-path (inverse winding) -> XPS-path (inverse even odd)]
             if (!pathIsMutable) {
                 xpsCompatiblePath = &modifiedPath;
@@ -1626,7 +1626,7 @@
             }
         }
         // The xpsCompatiblePath is now inverse even odd, so fall through.
-        case SkPath::kInverseEvenOdd_FillType: {
+        case SkPathFillType::kInverseEvenOdd: {
             const SkRect universe = SkRect::MakeLTRB(
                 0, 0,
                 this->fCurrentCanvasSize.fWidth,
diff --git a/src/xps/SkXPSDevice.h b/src/xps/SkXPSDevice.h
index f37bb2b..7dcfc65 100644
--- a/src/xps/SkXPSDevice.h
+++ b/src/xps/SkXPSDevice.h
@@ -40,8 +40,8 @@
 */
 class SkXPSDevice : public SkClipStackDevice {
 public:
-    SK_API SkXPSDevice(SkISize);
-    SK_API ~SkXPSDevice() override;
+    SK_SPI SkXPSDevice(SkISize);
+    SK_SPI ~SkXPSDevice() override;
 
     bool beginPortfolio(SkWStream* outputStream, IXpsOMObjectFactory*);
     /**
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index b738234..34aee51 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -176,11 +176,6 @@
 
 // What would raster do?
 static SkColor4f get_expected_color(SkColor4f orig, SkColorType ct) {
-    if (ct == kGray_8_SkColorType) {
-        // Prevent the premul raster graciously applies in this case
-        orig.fA = 1.0;
-    }
-
     SkAlphaType at = SkColorTypeIsAlwaysOpaque(ct) ? kOpaque_SkAlphaType
                                                    : kPremul_SkAlphaType;
 
@@ -657,14 +652,6 @@
                 }
 
                 {
-                    // GL has difficulties reading back from these combinations. In particular,
-                    // reading back kGray_8 is a mess.
-                    if (GrBackendApi::kOpenGL == context->backend()) {
-                        if (kAlpha_8_SkColorType == combo.fColorType ||
-                            kGray_8_SkColorType == combo.fColorType) {
-                            continue;
-                        }
-                    }
 
                     auto createWithColorMtd = [colorType](GrContext* context,
                                                           const SkColor4f& color,
@@ -687,42 +674,43 @@
 
                         return result;
                     };
-
+                    // We make our comparison color using SkPixmap::erase(color) on a pixmap of
+                    // combo.fColorType and then calling SkPixmap::readPixels(). erase() will premul
+                    // the color passed to it. However, createBackendTexture() that takes a
+                    // SkColor4f is color type / alpha type unaware and will simply compute
+                    // luminance from the r, g, b, channels.
+                    SkColor4f color = combo.fColor;
+                    if (colorType == kGray_8_SkColorType) {
+                        color = {color.fR * color.fA,
+                                 color.fG * color.fA,
+                                 color.fB * color.fA,
+                                 1.f};
+                    }
                     test_color_init(context, reporter, createWithColorMtd,
-                                    SkColorTypeToGrColorType(colorType),
-                                    combo.fColor, mipMapped, renderable);
+                                    SkColorTypeToGrColorType(colorType), color, mipMapped,
+                                    renderable);
                 }
 
-                // Gray_8 is problematic. In the colorInit tests there is ambiguity when
-                // mapping from format to colorType (since R8 or A8 could be either Alpha_8
-                // or Gray_8). To compensate for this ambiguity we feed in colors with
-                // R==G==B==A. If we actually do know the colorType (as is the case
-                // in the SkPixmap case, there is no ambiguity but the two test expectations
-                // now collide.
-                // For now, skip the SkPixmap tests. The real answer is to plumb the
-                // SkColorType down further in the color-init case.
-                if (colorType != kGray_8_SkColorType) {
-                    auto createWithSrcDataMtd = [](GrContext* context,
-                                                   const SkPixmap srcData[],
-                                                   int numLevels,
-                                                   GrRenderable renderable) {
-                        SkASSERT(srcData && numLevels);
-                        auto result = context->createBackendTexture(srcData, numLevels,
-                                                                    renderable, GrProtected::kNo);
-                        check_vk_layout(result, VkLayout::kReadOnlyOptimal);
+                auto createWithSrcDataMtd = [](GrContext* context,
+                                               const SkPixmap srcData[],
+                                               int numLevels,
+                                               GrRenderable renderable) {
+                    SkASSERT(srcData && numLevels);
+                    auto result = context->createBackendTexture(srcData, numLevels, renderable,
+                                                                GrProtected::kNo);
+                    check_vk_layout(result, VkLayout::kReadOnlyOptimal);
 #ifdef SK_DEBUG
-                        {
-                            auto format = context->defaultBackendFormat(srcData[0].colorType(),
-                                                                        renderable);
-                            SkASSERT(format == result.getBackendFormat());
-                        }
+                    {
+                        auto format =
+                                context->defaultBackendFormat(srcData[0].colorType(), renderable);
+                        SkASSERT(format == result.getBackendFormat());
+                    }
 #endif
-                        return result;
-                    };
+                    return result;
+                };
 
-                    test_pixmap_init(context, reporter, createWithSrcDataMtd, colorType,
-                                     mipMapped, renderable);
-                }
+                test_pixmap_init(context, reporter, createWithSrcDataMtd, colorType, mipMapped,
+                                 renderable);
             }
         }
     }
@@ -792,12 +780,10 @@
         }
 
         if (GrColorType::kBGRA_8888 == combo.fColorType) {
+            // We allow using a GL_RGBA8 texture as BGRA on desktop GL but not ES.
             if (GR_GL_RGBA8 == combo.fFormat && kGL_GrGLStandard != standard) {
                 continue;
             }
-            if (GR_GL_BGRA8 == combo.fFormat && kGL_GrGLStandard == standard) {
-                continue;
-            }
         }
 
         for (auto mipMapped : { GrMipMapped::kNo, GrMipMapped::kYes }) {
@@ -827,25 +813,53 @@
                 }
 
                 {
-                    // GL has difficulties reading back from these combinations
-                    if (GrColorType::kAlpha_8 == combo.fColorType) {
-                        continue;
-                    }
-                    if (GrRenderable::kYes != renderable) {
-                        continue;
+                    // We're creating backend textures without specifying a color type "view" of
+                    // them at the public API level. Therefore, Ganesh will not apply any swizzles
+                    // before writing the color to the texture. However, our validation code does
+                    // rely on interpreting the texture contents via a SkColorType and therefore
+                    // swizzles may be applied during the read step.
+                    // Ideally we'd update our validation code to use a "raw" read that doesn't
+                    // impose a color type but for now we just munge the data we upload to match the
+                    // expectation.
+                    GrSwizzle swizzle;
+                    switch (combo.fColorType) {
+                        case GrColorType::kAlpha_8:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        case GrColorType::kAlpha_16:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        case GrColorType::kAlpha_F16:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        default:
+                            break;
                     }
 
-                    auto createWithColorMtd = [format](GrContext* context,
-                                                       const SkColor4f& color,
-                                                       GrMipMapped mipMapped,
-                                                       GrRenderable renderable) {
-                        return context->createBackendTexture(32, 32, format, color,
+                    auto createWithColorMtd = [format, swizzle](GrContext* context,
+                                                                const SkColor4f& color,
+                                                                GrMipMapped mipMapped,
+                                                                GrRenderable renderable) {
+                        auto swizzledColor = swizzle.applyTo(color);
+                        return context->createBackendTexture(32, 32, format, swizzledColor,
                                                              mipMapped, renderable,
                                                              GrProtected::kNo);
                     };
+                    // We make our comparison color using SkPixmap::erase(color) on a pixmap of
+                    // combo.fColorType and then calling SkPixmap::readPixels(). erase() will premul
+                    // the color passed to it. However, createBackendTexture() that takes a
+                    // SkColor4f is color type/alpha type unaware and will simply compute luminance
+                    //from the r, g, b, channels.
+                    SkColor4f color = combo.fColor;
+                    if (combo.fColorType == GrColorType::kGray_8) {
+                        color = {color.fR * color.fA,
+                                 color.fG * color.fA,
+                                 color.fB * color.fA,
+                                 1.f};
+                    }
 
-                    test_color_init(context, reporter, createWithColorMtd,
-                                    combo.fColorType, combo.fColor, mipMapped, renderable);
+                    test_color_init(context, reporter, createWithColorMtd, combo.fColorType, color,
+                                    mipMapped, renderable);
                 }
             }
         }
diff --git a/tests/BulkRectTest.cpp b/tests/BulkRectTest.cpp
new file mode 100644
index 0000000..f81c97b
--- /dev/null
+++ b/tests/BulkRectTest.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "src/core/SkBlendModePriv.h"
+#include "src/gpu/GrClip.h"
+#include "src/gpu/GrContextPriv.h"
+#include "src/gpu/GrRenderTargetContext.h"
+#include "src/gpu/ops/GrFillRectOp.h"
+#include "src/gpu/ops/GrTextureOp.h"
+#include "tests/Test.h"
+
+static std::unique_ptr<GrRenderTargetContext> new_RTC(GrContext* context) {
+    return context->priv().makeDeferredRenderTargetContext(SkBackingFit::kExact, 128, 128,
+                                                           GrColorType::kRGBA_8888, nullptr);
+}
+
+sk_sp<GrSurfaceProxy> create_proxy(GrContext* context) {
+    GrSurfaceDesc desc;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    desc.fWidth  = 128;
+    desc.fHeight = 128;
+
+    const GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat(
+                                                                           GrColorType::kRGBA_8888,
+                                                                           GrRenderable::kYes);
+
+    return context->priv().proxyProvider()->createProxy(
+        format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
+        SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
+}
+
+typedef GrQuadAAFlags (*PerQuadAAFunc)(int i);
+
+typedef void (*BulkRectTest)(skiatest::Reporter* reporter, GrContext* context,
+                             PerQuadAAFunc perQuadAA, GrAAType overallAA, SkBlendMode blendMode,
+                             int requestedTotNumQuads, int expectedNumOps);
+
+//-------------------------------------------------------------------------------------------------
+static void bulk_fill_rect_create_test(skiatest::Reporter* reporter, GrContext* context,
+                                       PerQuadAAFunc perQuadAA, GrAAType overallAA,
+                                       SkBlendMode blendMode,
+                                       int requestedTotNumQuads, int expectedNumOps) {
+
+    std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context);
+
+    auto quads = new GrRenderTargetContext::QuadSetEntry[requestedTotNumQuads];
+
+    for (int i = 0; i < requestedTotNumQuads; ++i) {
+        quads[i].fRect = SkRect::MakeWH(100.5f, 100.5f); // prevent the int non-AA optimization
+        quads[i].fColor = SK_PMColor4fWHITE;
+        quads[i].fLocalMatrix = SkMatrix::I();
+        quads[i].fAAFlags = perQuadAA(i);
+    }
+
+    GrPaint paint;
+    paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
+    GrFillRectOp::AddFillRectOps(rtc.get(), GrNoClip(), context, std::move(paint), overallAA,
+                                 SkMatrix::I(), quads, requestedTotNumQuads);
+
+    GrOpsTask* opsTask = rtc->testingOnly_PeekLastOpsTask();
+    int actualNumOps = opsTask->numOpChains();
+
+    int actualTotNumQuads = 0;
+
+    for (int i = 0; i < actualNumOps; ++i) {
+        const GrOp* tmp = opsTask->getChain(i);
+        REPORTER_ASSERT(reporter, tmp->classID() == GrFillRectOp::ClassID());
+        REPORTER_ASSERT(reporter, tmp->isChainTail());
+        actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads();
+    }
+
+    REPORTER_ASSERT(reporter, expectedNumOps == actualNumOps);
+    REPORTER_ASSERT(reporter, requestedTotNumQuads == actualTotNumQuads);
+
+    context->flush();
+
+    delete[] quads;
+}
+
+//-------------------------------------------------------------------------------------------------
+static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContext* context,
+                                          PerQuadAAFunc perQuadAA, GrAAType overallAA,
+                                          SkBlendMode blendMode,
+                                          int requestedTotNumQuads, int expectedNumOps) {
+
+    std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context);
+
+    sk_sp<GrSurfaceProxy> proxyA = create_proxy(context);
+    sk_sp<GrSurfaceProxy> proxyB = create_proxy(context);
+    GrSurfaceProxyView proxyViewA(std::move(proxyA), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA());
+    GrSurfaceProxyView proxyViewB(std::move(proxyB), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA());
+
+    auto set = new GrRenderTargetContext::TextureSetEntry[requestedTotNumQuads];
+
+    for (int i = 0; i < requestedTotNumQuads; ++i) {
+        // Alternate between two proxies to prevent op merging if the batch API was forced to submit
+        // one op at a time (to work, this does require that all fDstRects overlap).
+        set[i].fProxyView = i % 2 == 0 ? proxyViewA : proxyViewB;
+        set[i].fSrcAlphaType = kPremul_SkAlphaType;
+        set[i].fSrcRect = SkRect::MakeWH(100.0f, 100.0f);
+        set[i].fDstRect = SkRect::MakeWH(100.5f, 100.5f); // prevent the int non-AA optimization
+        set[i].fDstClipQuad = nullptr;
+        set[i].fPreViewMatrix = nullptr;
+        set[i].fAlpha = 1.0f;
+        set[i].fAAFlags = perQuadAA(i);
+    }
+
+    GrTextureOp::AddTextureSetOps(rtc.get(), GrNoClip(), context, set, requestedTotNumQuads,
+                                     GrSamplerState::Filter::kNearest,
+                                     GrTextureOp::Saturate::kYes,
+                                     blendMode,
+                                     overallAA,
+                                     SkCanvas::kStrict_SrcRectConstraint,
+                                     SkMatrix::I(), nullptr);
+
+    GrOpsTask* opsTask = rtc->testingOnly_PeekLastOpsTask();
+    int actualNumOps = opsTask->numOpChains();
+
+    int actualTotNumQuads = 0;
+
+    if (blendMode != SkBlendMode::kSrcOver ||
+        !context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
+        // In either of these two cases, GrTextureOp creates one op per quad instead. Since
+        // each entry alternates proxies but overlaps geometrically, this will prevent the ops
+        // from being merged back into fewer ops.
+        expectedNumOps = requestedTotNumQuads;
+    }
+    uint32_t expectedOpID = blendMode == SkBlendMode::kSrcOver ? GrTextureOp::ClassID()
+                                                               : GrFillRectOp::ClassID();
+    for (int i = 0; i < actualNumOps; ++i) {
+        const GrOp* tmp = opsTask->getChain(i);
+        REPORTER_ASSERT(reporter, tmp->classID() == expectedOpID);
+        REPORTER_ASSERT(reporter, tmp->isChainTail());
+        actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads();
+    }
+
+    REPORTER_ASSERT(reporter, expectedNumOps == actualNumOps);
+    REPORTER_ASSERT(reporter, requestedTotNumQuads == actualTotNumQuads);
+
+    context->flush();
+
+    delete[] set;
+}
+
+//-------------------------------------------------------------------------------------------------
+static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectTest test) {
+    // This is the simple case where there is no AA at all. We expect 2 non-AA clumps of quads.
+    {
+        auto noAA = [](int i) -> GrQuadAAFlags {
+            return GrQuadAAFlags::kNone;
+        };
+
+        static const int kNumExpectedOps = 2;
+
+        test(reporter, context, noAA, GrAAType::kNone, SkBlendMode::kSrcOver,
+             2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
+    }
+
+    // This is the same as the above case except the overall AA is kCoverage. However, since
+    // the per-quad AA is still none, all the quads should be downgraded to non-AA.
+    {
+        auto noAA = [](int i) -> GrQuadAAFlags {
+            return GrQuadAAFlags::kNone;
+        };
+
+        static const int kNumExpectedOps = 2;
+
+        test(reporter, context, noAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
+             2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps);
+    }
+
+    // This case has an overall AA of kCoverage but the per-quad AA alternates.
+    // We should end up with several aa-sized clumps
+    {
+        auto alternateAA = [](int i) -> GrQuadAAFlags {
+            return (i % 2) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
+        };
+
+        int numExpectedOps = 2*GrResourceProvider::MaxNumNonAAQuads() /
+                                                 GrResourceProvider::MaxNumAAQuads();
+
+        test(reporter, context, alternateAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
+             2*GrResourceProvider::MaxNumNonAAQuads(), numExpectedOps);
+    }
+
+    // In this case we have a run of MaxNumAAQuads non-AA quads and then AA quads. This
+    // exercises the case where we have a clump of quads that can't be upgraded to AA bc of
+    // its size. We expect one clump of non-AA quads followed by one clump of AA quads.
+    {
+        auto runOfNonAA = [](int i) -> GrQuadAAFlags {
+            return (i < GrResourceProvider::MaxNumAAQuads()) ? GrQuadAAFlags::kNone
+                                                             : GrQuadAAFlags::kAll;
+        };
+
+        static const int kNumExpectedOps = 2;
+
+        test(reporter, context, runOfNonAA, GrAAType::kCoverage, SkBlendMode::kSrcOver,
+             2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps);
+    }
+
+    // In this case we use a blend mode other than src-over, which hits the GrFillRectOp fallback
+    // code path for GrTextureOp. We pass in the expected results if batching was successful, to
+    // that bulk_fill_rect_create_test batches on all modes; bulk_texture_rect_create_test is
+    // responsible for revising its expectations.
+    {
+        auto fixedAA = [](int i) -> GrQuadAAFlags {
+            return GrQuadAAFlags::kAll;
+        };
+
+        static const int kNumExpectedOps = 2;
+
+        test(reporter, context, fixedAA, GrAAType::kCoverage, SkBlendMode::kSrcATop,
+             2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps);
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BulkFillRectTest, reporter, ctxInfo) {
+    run_test(ctxInfo.grContext(), reporter, bulk_fill_rect_create_test);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BulkTextureRectTest, reporter, ctxInfo) {
+    run_test(ctxInfo.grContext(), reporter, bulk_texture_rect_create_test);
+}
diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp
index 5b8bb1f..5da7e6a 100644
--- a/tests/ClipCubicTest.cpp
+++ b/tests/ClipCubicTest.cpp
@@ -177,7 +177,6 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0,0
     path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43430143));  //195.263f, 195.005f
     path.lineTo(SkBits2Float(0x43434343), SkBits2Float(0x43434343));  //195.263f, 195.263f
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 435c86e..21f57fb 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -288,10 +288,10 @@
             bool doInvA = SkToBool(invBits & 1);
             bool doInvB = SkToBool(invBits & 2);
 
-            pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
-                                       SkPath::kEvenOdd_FillType);
-            pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
-                                       SkPath::kEvenOdd_FillType);
+            pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
+                                       SkPathFillType::kEvenOdd);
+            pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
+                                       SkPathFillType::kEvenOdd);
 
             switch (primType) {
                 case SkClipStack::Element::DeviceSpaceType::kEmpty:
@@ -362,10 +362,10 @@
         SkPath clipA, clipB;
 
         clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
-        clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
+        clipA.setFillType(SkPathFillType::kInverseEvenOdd);
 
         clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
-        clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
+        clipB.setFillType(SkPathFillType::kInverseEvenOdd);
 
         stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
         stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
@@ -856,7 +856,7 @@
 
     SkPath path;
     path.addRect({30, 10, 40, 20});
-    path.setFillType(SkPath::kInverseWinding_FillType);
+    path.setFillType(SkPathFillType::kInverseWinding);
     stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
 
     REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
@@ -893,7 +893,7 @@
     if (invert) {
         SkPath path;
         path.addRoundRect(rect, rx, ry);
-        path.setFillType(SkPath::kInverseWinding_FillType);
+        path.setFillType(SkPathFillType::kInverseWinding);
         stack->clipPath(path, SkMatrix::I(), op, doAA);
     } else {
         SkRRect rrect;
@@ -907,7 +907,7 @@
     if (invert) {
         SkPath path;
         path.addRect(rect);
-        path.setFillType(SkPath::kInverseWinding_FillType);
+        path.setFillType(SkPathFillType::kInverseWinding);
         stack->clipPath(path, SkMatrix::I(), op, doAA);
     } else {
         stack->clipRect(rect, SkMatrix::I(), op, doAA);
@@ -919,7 +919,7 @@
     SkPath path;
     path.addOval(rect);
     if (invert) {
-        path.setFillType(SkPath::kInverseWinding_FillType);
+        path.setFillType(SkPathFillType::kInverseWinding);
     }
     stack->clipPath(path, SkMatrix::I(), op, doAA);
 };
@@ -1534,7 +1534,7 @@
     SkPath path;
     path.addCircle(10, 10, 8);
     path.addCircle(15, 15, 8);
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     static const char* kTag = GrClipStackClip::kMaskTestTag;
     GrResourceCache* cache = context->priv().getResourceCache();
diff --git a/tests/CodecPartialTest.cpp b/tests/CodecPartialTest.cpp
index 0ff5435..62b744de 100644
--- a/tests/CodecPartialTest.cpp
+++ b/tests/CodecPartialTest.cpp
@@ -170,6 +170,34 @@
     }
 }
 
+DEF_TEST(Codec_frameCountUpdatesInIncrementalDecode, r) {
+    sk_sp<SkData> file = GetResourceAsData("images/colorTables.gif");
+    size_t fileSize = file->size();
+    REPORTER_ASSERT(r, fileSize == 2829);
+    std::unique_ptr<SkCodec> fullCodec(SkCodec::MakeFromData(file));
+    REPORTER_ASSERT(r, fullCodec->getFrameCount() == 2);
+    const SkImageInfo info = standardize_info(fullCodec.get());
+
+    static const size_t n = 1000;
+    HaltingStream* stream = new HaltingStream(file, n);
+    // Note that we cheat and hold on to a pointer to stream, though it is owned by
+    // partialCodec.
+    auto partialCodec = SkCodec::MakeFromStream(std::unique_ptr<SkStream>(stream));
+    REPORTER_ASSERT(r, partialCodec->getFrameCount() == 1);
+
+    SkBitmap bitmap;
+    bitmap.allocPixels(info);
+    REPORTER_ASSERT(r, SkCodec::kSuccess ==
+            partialCodec->startIncrementalDecode(
+                info, bitmap.getPixels(), bitmap.rowBytes()));
+    REPORTER_ASSERT(r, SkCodec::kIncompleteInput ==
+            partialCodec->incrementalDecode());
+
+    REPORTER_ASSERT(r, partialCodec->getFrameCount() == 1);
+    stream->addNewData(fileSize - n);
+    REPORTER_ASSERT(r, partialCodec->getFrameCount() == 2);
+}
+
 // Verify that when decoding an animated gif byte by byte we report the correct
 // fRequiredFrame as soon as getFrameInfo reports the frame.
 DEF_TEST(Codec_requiredFrame, r) {
diff --git a/tests/ColorSpaceTest.cpp b/tests/ColorSpaceTest.cpp
index 24a05f6..f2a3ea7 100644
--- a/tests/ColorSpaceTest.cpp
+++ b/tests/ColorSpaceTest.cpp
@@ -334,3 +334,17 @@
 
     REPORTER_ASSERT(r, 0 == memcmp(&profile, skcms_sRGB_profile(), sizeof(skcms_ICCProfile)));
 }
+
+DEF_TEST(ColorSpace_classifyUnderflow, r) {
+    // crbug.com/1016183
+    skcms_TransferFunction fn;
+    fn.a = 1.0f;
+    fn.b = 0.0f;
+    fn.c = 0.0f;
+    fn.d = 0.0f;
+    fn.e = 0.0f;
+    fn.f = 0.0f;
+    fn.g = INT_MIN;
+    sk_sp<SkColorSpace> bad = SkColorSpace::MakeRGB(fn, SkNamedGamut::kSRGB);
+    REPORTER_ASSERT(r, bad == nullptr);
+}
diff --git a/tests/CopySurfaceTest.cpp b/tests/CopySurfaceTest.cpp
index aba889d..c4c9af7 100644
--- a/tests/CopySurfaceTest.cpp
+++ b/tests/CopySurfaceTest.cpp
@@ -116,7 +116,7 @@
                                 } else if (dRenderable == GrRenderable::kYes) {
                                     SkASSERT(dstContext->asRenderTargetContext());
                                     result = dstContext->asRenderTargetContext()->blitTexture(
-                                            src.get(), grColorType, srcRect, dstPoint);
+                                            src.get(), srcRect, dstPoint);
                                 }
 
                                 bool expectedResult = true;
diff --git a/tests/DefaultPathRendererTest.cpp b/tests/DefaultPathRendererTest.cpp
index 7b5f903..40a8578 100644
--- a/tests/DefaultPathRendererTest.cpp
+++ b/tests/DefaultPathRendererTest.cpp
@@ -52,11 +52,11 @@
     return bm;
 }
 
-static SkPath make_path(const SkRect& outer, int inset, SkPath::FillType fill) {
+static SkPath make_path(const SkRect& outer, int inset, SkPathFillType fill) {
     SkPath p;
 
-    p.addRect(outer, SkPath::kCW_Direction);
-    p.addRect(outer.makeInset(inset, inset), SkPath::kCCW_Direction);
+    p.addRect(outer, SkPathDirection::kCW);
+    p.addRect(outer.makeInset(inset, inset), SkPathDirection::kCCW);
     p.setFillType(fill);
     return p;
 }
@@ -77,9 +77,9 @@
 
 static void run_test(GrContext* ctx, skiatest::Reporter* reporter) {
     SkPath invPath = make_path(SkRect::MakeXYWH(0, 0, kBigSize, kBigSize),
-                               kBigSize/2-1, SkPath::kInverseWinding_FillType);
+                               kBigSize/2-1, SkPathFillType::kInverseWinding);
     SkPath path = make_path(SkRect::MakeXYWH(0, 0, kBigSize, kBigSize),
-                            kPad, SkPath::kWinding_FillType);
+                            kPad, SkPathFillType::kWinding);
 
     GrStyle style(SkStrokeRec::kFill_InitStyle);
 
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index e816ba6..d31fcb6 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -188,6 +188,7 @@
                                         ? GrMipMapped::kNo
                                         : GrMipMapped(fShouldCreateMipMaps);
 
+#ifdef SK_GL
         if (fUsesGLFBO0) {
             if (GrBackendApi::kOpenGL != context->backend()) {
                 return nullptr;
@@ -210,6 +211,7 @@
             SkASSERT(result->isCompatible(c));
             return result;
         }
+#endif
 
         *backend = context->createBackendTexture(fWidth, fHeight, fColorType,
                                                  SkColors::kTransparent,
@@ -998,6 +1000,7 @@
     }
 }
 
+#ifdef SK_GL
 ////////////////////////////////////////////////////////////////////////////////
 // Check that the texture-specific flags (i.e., for external & rectangle textures) work
 // for promise images. As such, this is a GL-only test.
@@ -1043,6 +1046,7 @@
         }
     }
 }
+#endif  // SK_GL
 
 ////////////////////////////////////////////////////////////////////////////////
 // Test colorType and pixelConfig compatibility.
diff --git a/tests/DescriptorTest.cpp b/tests/DescriptorTest.cpp
index eab3673..837a37f 100644
--- a/tests/DescriptorTest.cpp
+++ b/tests/DescriptorTest.cpp
@@ -122,3 +122,17 @@
     SkDescriptorTestHelper::SetCount(desc.get(), 1);
     REPORTER_ASSERT(r, !desc->isValid());
 }
+
+DEF_TEST(Descriptor_entry_over_end, r) {
+    auto desc = SkDescriptor::Alloc(36);
+    desc->init();
+
+    // Make the start of the Entry be in the SkDescriptor, but the second half falls out side the
+    // SkDescriptor. So: 12 (for descriptor) + 8 (for entry) + 12 (for entry length) = 32. An
+    // An Entry is 8 bytes, so 4 bytes are < 36 and 4 bytes > 36.
+    desc->addEntry(kEffects_SkDescriptorTag, 12, nullptr);
+
+    SkDescriptorTestHelper::SetLength(desc.get(), 36);
+    SkDescriptorTestHelper::SetCount(desc.get(), 2);
+    REPORTER_ASSERT(r, !desc->isValid());
+}
diff --git a/tests/DrawOpAtlasTest.cpp b/tests/DrawOpAtlasTest.cpp
index ec6068c..826d53d 100644
--- a/tests/DrawOpAtlasTest.cpp
+++ b/tests/DrawOpAtlasTest.cpp
@@ -211,10 +211,13 @@
     TestingUploadTarget uploadTarget;
 
     GrOpFlushState flushState(gpu, resourceProvider, uploadTarget.writeableTokenTracker());
+
+    GrSurfaceProxyView surfaceView = rtc->outputSurfaceView();
     GrOpFlushState::OpArgs opArgs(op.get(),
-                                  rtc->asRenderTargetProxy(),
+                                  &surfaceView,
                                   nullptr,
-                                  GrXferProcessor::DstProxy(nullptr, SkIPoint::Make(0, 0)));
+                                  GrXferProcessor::DstProxyView(GrSurfaceProxyView(),
+                                                                SkIPoint::Make(0, 0)));
 
     // Cripple the atlas manager so it can't allocate any pages. This will force a failure
     // in the preparation of the text op
diff --git a/tests/EGLImageTest.cpp b/tests/EGLImageTest.cpp
index 7f4c6f8..80497c0 100644
--- a/tests/EGLImageTest.cpp
+++ b/tests/EGLImageTest.cpp
@@ -21,6 +21,8 @@
 #include "tools/gpu/GrContextFactory.h"
 #include "tools/gpu/gl/GLTestContext.h"
 
+#ifdef SK_GL
+
 using sk_gpu_test::GLTestContext;
 
 static void cleanup(GLTestContext* glctx0, GrGLuint texID0, GLTestContext* glctx1,
@@ -197,3 +199,5 @@
 
     cleanup(glCtx0, externalTexture.fID, glCtx1.get(), context1, &backendTexture1, image);
 }
+
+#endif  // SK_GL
diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp
index 8212fc4..06c3cea 100644
--- a/tests/EmptyPathTest.cpp
+++ b/tests/EmptyPathTest.cpp
@@ -126,11 +126,11 @@
     static void (*gMakeProc[])(SkPath*) = {
         make_empty, make_M, make_MM, make_MZM, make_L, make_Q, make_C
     };
-    static SkPath::FillType gFills[] = {
-        SkPath::kWinding_FillType,
-        SkPath::kEvenOdd_FillType,
-        SkPath::kInverseWinding_FillType,
-        SkPath::kInverseEvenOdd_FillType
+    static SkPathFillType gFills[] = {
+        SkPathFillType::kWinding,
+        SkPathFillType::kEvenOdd,
+        SkPathFillType::kInverseWinding,
+        SkPathFillType::kInverseEvenOdd
     };
     for (int doClose = 0; doClose < 2; ++doClose) {
         for  (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) {
diff --git a/tests/FillPathTest.cpp b/tests/FillPathTest.cpp
index f6e1c5e..04bcb01 100644
--- a/tests/FillPathTest.cpp
+++ b/tests/FillPathTest.cpp
@@ -41,7 +41,7 @@
         .quadTo(SkIntToScalar(width/2), SkIntToScalar(height),
               SkIntToScalar(width), 0.0f)
         .close()
-        .setFillType(SkPath::kInverseWinding_FillType);
+        .setFillType(SkPathFillType::kInverseWinding);
     SkScan::FillPath(path, clip, &blitter);
 
     REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines);
diff --git a/tests/FontTest.cpp b/tests/FontTest.cpp
new file mode 100644
index 0000000..1ae8aa8
--- /dev/null
+++ b/tests/FontTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "include/core/SkFont.h"
+#include "src/core/SkAutoMalloc.h"
+#include "src/core/SkFontPriv.h"
+#include "src/core/SkReadBuffer.h"
+#include "src/core/SkWriteBuffer.h"
+#include "tests/Test.h"
+
+static SkFont serialize_deserialize(const SkFont& font, skiatest::Reporter* reporter) {
+    SkBinaryWriteBuffer wb;
+    SkFontPriv::Flatten(font, wb);
+
+    size_t size = wb.bytesWritten();
+    SkAutoMalloc storage(size);
+    wb.writeToMemory(storage.get());
+
+    SkReadBuffer rb(storage.get(), size);
+
+    SkFont clone;
+    REPORTER_ASSERT(reporter, SkFontPriv::Unflatten(&clone, rb));
+    return clone;
+}
+
+enum {
+    kForceAutoHinting      = 1 << 0,
+    kEmbeddedBitmaps       = 1 << 1,
+    kSubpixel              = 1 << 2,
+    kLinearMetrics         = 1 << 3,
+    kEmbolden              = 1 << 4,
+    kBaselineSnap          = 1 << 5,
+
+    kAllBits = 0x3F,
+};
+
+static void apply_flags(SkFont* font, unsigned flags) {
+    font->setForceAutoHinting(SkToBool(flags & kForceAutoHinting));
+    font->setEmbeddedBitmaps( SkToBool(flags & kEmbeddedBitmaps));
+    font->setSubpixel(        SkToBool(flags & kSubpixel));
+    font->setLinearMetrics(   SkToBool(flags & kLinearMetrics));
+    font->setEmbolden(        SkToBool(flags & kEmbolden));
+    font->setBaselineSnap(    SkToBool(flags & kBaselineSnap));
+}
+
+DEF_TEST(Font_flatten, reporter) {
+    const float sizes[] = {0, 0.001f, 1, 10, 10.001f, 100, 100000, 100000.01f};
+    const float scales[] = {-5, -1, 0, 1, 5};
+    const float skews[] = {-5, -1, 0, 1, 5};
+    const SkFont::Edging edges[] = {
+        SkFont::Edging::kAlias, SkFont::Edging::kAntiAlias, SkFont::Edging::kSubpixelAntiAlias
+    };
+    const SkFontHinting hints[] = {
+        SkFontHinting::kNone, SkFontHinting::kSlight, SkFontHinting::kNormal, SkFontHinting::kFull
+    };
+
+    SkFont font;
+    for (float size : sizes) {
+        font.setSize(size);
+        for (float scale : scales) {
+            font.setScaleX(scale);
+            for (float skew : skews) {
+                font.setSkewX(skew);
+                for (auto edge : edges) {
+                    font.setEdging(edge);
+                    for (auto hint : hints) {
+                        font.setHinting(hint);
+                        for (unsigned flags = 0; flags <= kAllBits; ++flags) {
+                            apply_flags(&font, flags);
+
+                            SkFont clone = serialize_deserialize(font, reporter);
+                            REPORTER_ASSERT(reporter, font == clone);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/GpuDrawPathTest.cpp b/tests/GpuDrawPathTest.cpp
index 22fecd7..5431aaa 100644
--- a/tests/GpuDrawPathTest.cpp
+++ b/tests/GpuDrawPathTest.cpp
@@ -65,8 +65,8 @@
 
     SkPath oval1, oval2;
     const SkRect rect = SkRect::MakeWH(100, 50);
-    oval1.addOval(rect, SkPath::kCW_Direction);
-    oval2.addOval(rect, SkPath::kCCW_Direction);
+    oval1.addOval(rect, SkPathDirection::kCW);
+    oval2.addOval(rect, SkPathDirection::kCCW);
 
     fill_and_stroke(canvas, oval1, oval2, nullptr);
 
diff --git a/tests/GrGLExtensionsTest.cpp b/tests/GrGLExtensionsTest.cpp
index 480e624..4c95706 100644
--- a/tests/GrGLExtensionsTest.cpp
+++ b/tests/GrGLExtensionsTest.cpp
@@ -11,6 +11,8 @@
 #include "src/gpu/gl/GrGLDefines.h"
 #include "tests/Test.h"
 
+#ifdef SK_GL
+
 const GrGLubyte* simpleGetString(GrGLenum name) {
     return (const GrGLubyte*)(name == GR_GL_VERSION ? "3.0" : "");
 }
@@ -46,3 +48,5 @@
     REPORTER_ASSERT(reporter, ext.remove("test_extension_1"));
     REPORTER_ASSERT(reporter, !ext.has("test_extension_1"));
 }
+
+#endif  // SK_GL
diff --git a/tests/GrMeshTest.cpp b/tests/GrMeshTest.cpp
index d45aa7f..b2338e5 100644
--- a/tests/GrMeshTest.cpp
+++ b/tests/GrMeshTest.cpp
@@ -62,7 +62,7 @@
     sk_sp<const GrBuffer> fIndexBuffer;
     sk_sp<const GrBuffer> fInstBuffer;
 
-    void drawMesh(const GrMesh& mesh);
+    void drawMesh(const GrMesh& mesh, GrPrimitiveType);
 
 private:
     GrOpFlushState* fState;
@@ -162,7 +162,7 @@
                      GrMesh mesh(GrPrimitiveType::kTriangles);
                      mesh.setNonIndexedNonInstanced(kBoxCountX * 6);
                      mesh.setVertexData(helper->fVertBuffer, y * kBoxCountX * 6);
-                     helper->drawMesh(mesh);
+                     helper->drawMesh(mesh, GrPrimitiveType::kTriangles);
                  }
              });
 
@@ -187,7 +187,7 @@
                                     baseRepetition * 4, (baseRepetition + repetitionCount) * 4 - 1,
                                     GrPrimitiveRestart::kNo);
                     mesh.setVertexData(helper->fVertBuffer, (i - baseRepetition) * 4);
-                    helper->drawMesh(mesh);
+                    helper->drawMesh(mesh, GrPrimitiveType::kTriangles);
 
                     baseRepetition = (baseRepetition + 1) % 3;
                     i += repetitionCount;
@@ -209,7 +209,7 @@
                     mesh.setIndexedPatterned(helper->fIndexBuffer, 6, 4, kBoxCountX,
                                              kIndexPatternRepeatCount);
                     mesh.setVertexData(helper->fVertBuffer, y * kBoxCountX * 4);
-                    helper->drawMesh(mesh);
+                    helper->drawMesh(mesh, GrPrimitiveType::kTriangles);
                 }
              });
 
@@ -236,8 +236,10 @@
                      // null vertex buffer. setIndexedInstanced intentionally does not support a
                      // base index.
                      for (int y = 0; y < kBoxCountY; ++y) {
-                         GrMesh mesh(indexed ? GrPrimitiveType::kTriangles
-                                     : GrPrimitiveType::kTriangleStrip);
+
+                         GrPrimitiveType primitiveType = indexed ? GrPrimitiveType::kTriangles
+                                                                 : GrPrimitiveType::kTriangleStrip;
+                         GrMesh mesh(primitiveType);
                          if (indexed) {
                              VALIDATE(helper->fIndexBuffer);
                              mesh.setIndexedInstanced(helper->fIndexBuffer, 6, helper->fInstBuffer,
@@ -264,7 +266,7 @@
                                  mesh.setVertexData(helper->fVertBuffer2, 2);
                                  break;
                          }
-                         helper->drawMesh(mesh);
+                         helper->drawMesh(mesh, primitiveType);
                      }
                  });
     }
@@ -319,6 +321,27 @@
 
 class GrMeshTestProcessor : public GrGeometryProcessor {
 public:
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena, bool instanced, bool hasVertexBuffer) {
+        return arena->make<GrMeshTestProcessor>(instanced, hasVertexBuffer);
+    }
+
+    const char* name() const override { return "GrMeshTestProcessor"; }
+
+    const Attribute& inColor() const {
+        return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
+    }
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
+        b->add32(fInstanceLocation.isInitialized());
+        b->add32(fVertexPosition.isInitialized());
+    }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+private:
+    friend class GLSLMeshTestProcessor;
+    friend class ::SkArenaAlloc; // for access to ctor
+
     GrMeshTestProcessor(bool instanced, bool hasVertexBuffer)
             : INHERITED(kGrMeshTestProcessor_ClassID) {
         if (instanced) {
@@ -336,33 +359,18 @@
         }
     }
 
-    const char* name() const override { return "GrMeshTest Processor"; }
-
-    const Attribute& inColor() const {
-        return fVertexColor.isInitialized() ? fVertexColor : fInstanceColor;
-    }
-
-    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
-        b->add32(fInstanceLocation.isInitialized());
-        b->add32(fVertexPosition.isInitialized());
-    }
-
-    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
-
-private:
     Attribute fVertexPosition;
     Attribute fVertexColor;
 
     Attribute fInstanceLocation;
     Attribute fInstanceColor;
 
-    friend class GLSLMeshTestProcessor;
     typedef GrGeometryProcessor INHERITED;
 };
 
 class GLSLMeshTestProcessor : public GrGLSLGeometryProcessor {
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) final {}
+                 const CoordTransformRange& transformIter) final {}
 
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
         const GrMeshTestProcessor& mp = args.fGP.cast<GrMeshTestProcessor>();
@@ -408,15 +416,19 @@
             kIndexPattern, 6, kIndexPatternRepeatCount, 4, gIndexBufferKey);
 }
 
-void DrawMeshHelper::drawMesh(const GrMesh& mesh) {
+void DrawMeshHelper::drawMesh(const GrMesh& mesh, GrPrimitiveType primitiveType) {
     GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kSrc, GrSwizzle::RGBA());
-    GrMeshTestProcessor mtp(mesh.isInstanced(), mesh.hasVertexData());
 
-    GrProgramInfo programInfo(fState->drawOpArgs().numSamples(),
-                              fState->drawOpArgs().origin(),
-                              pipeline,
+    GrGeometryProcessor* mtp = GrMeshTestProcessor::Make(fState->allocator(),
+                                                         mesh.isInstanced(), mesh.hasVertexData());
+
+    GrProgramInfo programInfo(fState->proxy()->numSamples(),
+                              fState->proxy()->numStencilSamples(),
+                              fState->proxy()->backendFormat(),
+                              fState->view()->origin(),
+                              &pipeline,
                               mtp,
-                              nullptr, nullptr, 0);
+                              nullptr, nullptr, 0, primitiveType);
 
     fState->opsRenderPass()->draw(programInfo, &mesh, 1,
                                   SkRect::MakeIWH(kImageWidth, kImageHeight));
diff --git a/tests/GrMipMappedTest.cpp b/tests/GrMipMappedTest.cpp
index 6bb6afd..d911dbb 100644
--- a/tests/GrMipMappedTest.cpp
+++ b/tests/GrMipMappedTest.cpp
@@ -340,7 +340,7 @@
 // Create a new render target and draw 'mipmapProxy' into it using the provided 'filter'.
 static std::unique_ptr<GrRenderTargetContext> draw_mipmap_into_new_render_target(
         GrDrawingManager* drawingManager, GrProxyProvider* proxyProvider, GrColorType colorType,
-        sk_sp<GrTextureProxy> mipmapProxy, GrSamplerState::Filter filter) {
+        SkAlphaType alphaType, sk_sp<GrTextureProxy> mipmapProxy, GrSamplerState::Filter filter) {
     GrSurfaceDesc desc;
     desc.fWidth = 1;
     desc.fHeight = 1;
@@ -350,9 +350,10 @@
             GrMipMapped::kNo, SkBackingFit::kApprox, SkBudgeted::kYes, GrProtected::kNo);
     auto rtc = drawingManager->makeRenderTargetContext(
             std::move(renderTarget), colorType, nullptr, nullptr, true);
-    rtc->drawTexture(GrNoClip(), mipmapProxy, colorType, filter, SkBlendMode::kSrcOver,
+    rtc->drawTexture(GrNoClip(), mipmapProxy, colorType, alphaType, filter, SkBlendMode::kSrcOver,
                      {1,1,1,1}, SkRect::MakeWH(4, 4), SkRect::MakeWH(1,1), GrAA::kYes,
-                     GrQuadAAFlags::kAll, SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
+                     GrQuadAAFlags::kAll, SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(),
+                     nullptr);
     return rtc;
 }
 
@@ -380,6 +381,7 @@
                 kRGBA_8888_SkColorType, GrRenderable::kYes);
         GrPixelConfig config = kRGBA_8888_GrPixelConfig;
         GrColorType colorType = GrColorType::kRGBA_8888;
+        SkAlphaType alphaType = kPremul_SkAlphaType;
 
         GrDrawingManager* drawingManager = context->priv().drawingManager();
         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
@@ -410,8 +412,8 @@
         REPORTER_ASSERT(reporter, !mipmapProxy->mipMapsAreDirty());
 
         // Draw the dirty mipmap texture into a render target.
-        auto rtc1 = draw_mipmap_into_new_render_target(
-                drawingManager, proxyProvider, colorType, mipmapProxy, Filter::kMipMap);
+        auto rtc1 = draw_mipmap_into_new_render_target(drawingManager, proxyProvider, colorType,
+                                                       alphaType, mipmapProxy, Filter::kMipMap);
 
         // Mipmaps should have gotten marked dirty during makeClosed, then marked clean again as
         // soon as a GrTextureResolveRenderTask was inserted. The way we know they were resolved is
@@ -424,8 +426,8 @@
         REPORTER_ASSERT(reporter, !mipmapProxy->mipMapsAreDirty());
 
         // Draw the now-clean mipmap texture into a second target.
-        auto rtc2 = draw_mipmap_into_new_render_target(
-                drawingManager, proxyProvider, colorType, mipmapProxy, Filter::kMipMap);
+        auto rtc2 = draw_mipmap_into_new_render_target(drawingManager, proxyProvider, colorType,
+                                                       alphaType, mipmapProxy, Filter::kMipMap);
 
         // Make sure the mipmap texture still has the same regen task.
         REPORTER_ASSERT(reporter, mipmapProxy->getLastRenderTask() == initialMipmapRegenTask);
@@ -451,8 +453,8 @@
         REPORTER_ASSERT(reporter, !mipmapProxy->mipMapsAreDirty());
 
         // Draw the dirty mipmap texture into a render target, but don't do mipmap filtering.
-        rtc1 = draw_mipmap_into_new_render_target(
-                drawingManager, proxyProvider, colorType, mipmapProxy, Filter::kBilerp);
+        rtc1 = draw_mipmap_into_new_render_target(drawingManager, proxyProvider, colorType,
+                                                  alphaType, mipmapProxy, Filter::kBilerp);
 
         // Mipmaps should have gotten marked dirty during makeClosed() when adding the dependency.
         // Since the last draw did not use mips, they will not have been regenerated and should
@@ -464,8 +466,8 @@
                 mipmapRTC->testingOnly_PeekLastOpsTask() == mipmapProxy->getLastRenderTask());
 
         // Draw the stil-dirty mipmap texture into a second target with mipmap filtering.
-        rtc2 = draw_mipmap_into_new_render_target(
-                drawingManager, proxyProvider, colorType, mipmapProxy, Filter::kMipMap);
+        rtc2 = draw_mipmap_into_new_render_target(drawingManager, proxyProvider, colorType,
+                                                  alphaType, mipmapProxy, Filter::kMipMap);
 
         // Make sure the mipmap texture now has a new last render task that regenerates the mips,
         // and that the mipmaps are now clean.
diff --git a/tests/GrPipelineDynamicStateTest.cpp b/tests/GrPipelineDynamicStateTest.cpp
index 485aa14..9ab35f0 100644
--- a/tests/GrPipelineDynamicStateTest.cpp
+++ b/tests/GrPipelineDynamicStateTest.cpp
@@ -60,9 +60,8 @@
 
 class GrPipelineDynamicStateTestProcessor : public GrGeometryProcessor {
 public:
-    GrPipelineDynamicStateTestProcessor()
-            : INHERITED(kGrPipelineDynamicStateTestProcessor_ClassID) {
-        this->setVertexAttributes(kAttributes, SK_ARRAY_COUNT(kAttributes));
+    static GrGeometryProcessor* Make(SkArenaAlloc* arena) {
+        return arena->make<GrPipelineDynamicStateTestProcessor>();
     }
 
     const char* name() const override { return "GrPipelineDynamicStateTest Processor"; }
@@ -75,6 +74,13 @@
     const Attribute& inColor() const { return kAttributes[1]; }
 
 private:
+    friend class ::SkArenaAlloc; // for access to ctor
+
+    GrPipelineDynamicStateTestProcessor()
+            : INHERITED(kGrPipelineDynamicStateTestProcessor_ClassID) {
+        this->setVertexAttributes(kAttributes, SK_ARRAY_COUNT(kAttributes));
+    }
+
     static constexpr Attribute kAttributes[] = {
         {"vertex", kFloat2_GrVertexAttribType, kHalf2_GrSLType},
         {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType},
@@ -87,7 +93,7 @@
 
 class GLSLPipelineDynamicStateTestProcessor : public GrGLSLGeometryProcessor {
     void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
-                 FPCoordTransformIter&& transformIter) final {}
+                 const CoordTransformRange&) final {}
 
     void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
         const GrPipelineDynamicStateTestProcessor& mp =
@@ -153,14 +159,16 @@
         GrPipeline::DynamicStateArrays dynamicState;
         dynamicState.fScissorRects = kDynamicScissors;
 
-        GrPipelineDynamicStateTestProcessor primProc;
+        auto geomProc = GrPipelineDynamicStateTestProcessor::Make(flushState->allocator());
 
-        GrProgramInfo programInfo(flushState->drawOpArgs().numSamples(),
-                                  flushState->drawOpArgs().origin(),
-                                  pipeline,
-                                  primProc,
+        GrProgramInfo programInfo(flushState->proxy()->numSamples(),
+                                  flushState->proxy()->numStencilSamples(),
+                                  flushState->proxy()->backendFormat(),
+                                  flushState->view()->origin(),
+                                  &pipeline,
+                                  geomProc,
                                   nullptr,
-                                  &dynamicState, 0);
+                                  &dynamicState, 0, GrPrimitiveType::kTriangleStrip);
 
         flushState->opsRenderPass()->draw(programInfo, meshes.begin(), 4,
                                           SkRect::MakeIWH(kScreenSize, kScreenSize));
diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp
index 533ff0f..a5756bf 100644
--- a/tests/GrPorterDuffTest.cpp
+++ b/tests/GrPorterDuffTest.cpp
@@ -997,12 +997,14 @@
         ctx->createBackendTexture(100, 100, kRGBA_8888_SkColorType, SkColors::kTransparent,
                                   GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
 
-    GrXferProcessor::DstProxy fakeDstProxy;
+    GrXferProcessor::DstProxyView fakeDstProxyView;
     {
         sk_sp<GrTextureProxy> proxy = proxyProvider->wrapBackendTexture(
                 backendTex, GrColorType::kRGBA_8888, kTopLeft_GrSurfaceOrigin,
                 kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType);
-        fakeDstProxy.setProxy(std::move(proxy));
+        GrSwizzle swizzle = caps.getTextureSwizzle(backendTex.getBackendFormat(),
+                                                   GrColorType::kRGBA_8888);
+        fakeDstProxyView.setProxyView({std::move(proxy), kTopLeft_GrSurfaceOrigin, swizzle});
     }
 
     static const GrProcessorAnalysisColor colorInputs[] = {
diff --git a/tests/GrQuadBufferTest.cpp b/tests/GrQuadBufferTest.cpp
index 928438f..c6ba08f 100644
--- a/tests/GrQuadBufferTest.cpp
+++ b/tests/GrQuadBufferTest.cpp
@@ -112,16 +112,17 @@
     auto iter = buffer.iterator();
     while(iter.next()) {
         // Each entry always has the device quad
-        assert_quad_eq(r, expectedDeviceQuads[i], iter.deviceQuad());
+        assert_quad_eq(r, expectedDeviceQuads[i], *iter.deviceQuad());
         assert_metadata_eq(r, {2 * i, 3.f * i}, iter.metadata());
 
         if (i % 2 == 0) {
             // Confirm local quads included on even entries
             ASSERT(iter.isLocalValid());
-            assert_quad_eq(r, expectedLocalQuads[i], iter.localQuad());
+            assert_quad_eq(r, expectedLocalQuads[i], *iter.localQuad());
         } else {
             // Should not have locals
             ASSERT(!iter.isLocalValid());
+            ASSERT(!iter.localQuad());
         }
 
         i++;
@@ -161,25 +162,27 @@
     while(iter.next()) {
         if (i < kQuadCount) {
             // First half should match original buffer1
-            assert_quad_eq(r, quadsA[i], iter.deviceQuad());
+            assert_quad_eq(r, quadsA[i], *iter.deviceQuad());
             assert_metadata_eq(r, {i, 2.f * i}, iter.metadata());
             if (i % 2 == 0) {
                 ASSERT(iter.isLocalValid());
-                assert_quad_eq(r, quadsB[i], iter.localQuad());
+                assert_quad_eq(r, quadsB[i], *iter.localQuad());
             } else {
                 ASSERT(!iter.isLocalValid());
+                ASSERT(!iter.localQuad());
             }
 
         } else {
             // Second half should match buffer2
             int j = i - kQuadCount;
-            assert_quad_eq(r, quadsB[j], iter.deviceQuad());
+            assert_quad_eq(r, quadsB[j], *iter.deviceQuad());
             assert_metadata_eq(r, {2 * j, 0.5f * j}, iter.metadata());
             if (j % 2 == 0) {
                 ASSERT(!iter.isLocalValid());
+                ASSERT(!iter.localQuad());
             } else {
                 ASSERT(iter.isLocalValid());
-                assert_quad_eq(r, quadsA[j], iter.localQuad());
+                assert_quad_eq(r, quadsA[j], *iter.localQuad());
             }
         }
 
@@ -221,12 +224,13 @@
         assert_metadata_eq(r, {2 * i, 0.5f * i}, iter.metadata());
 
         // Quad coordinates are unchanged
-        assert_quad_eq(r, quad, iter.deviceQuad());
+        assert_quad_eq(r, quad, *iter.deviceQuad());
         if (i % 2 == 0) {
             ASSERT(iter.isLocalValid());
-            assert_quad_eq(r, quad, iter.localQuad());
+            assert_quad_eq(r, quad, *iter.localQuad());
         } else {
             ASSERT(!iter.isLocalValid());
+            ASSERT(!iter.localQuad());
         }
         i++;
     }
diff --git a/tests/GrShapeTest.cpp b/tests/GrShapeTest.cpp
index 548bd04..01bb540 100644
--- a/tests/GrShapeTest.cpp
+++ b/tests/GrShapeTest.cpp
@@ -119,7 +119,7 @@
     // The asRRect() output params are all initialized just to silence compiler warnings about
     // uninitialized variables.
     SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
-    SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
+    SkPathDirection dirA = SkPathDirection::kCW, dirB = SkPathDirection::kCW;
     unsigned startA = ~0U, startB = ~0U;
     bool invertedA = true, invertedB = true;
 
@@ -145,8 +145,8 @@
         ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
     }
     bool ignoreWindingVsEvenOdd = false;
-    if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
-        SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
+    if (SkPathFillType_ConvertToNonInverse(pathA.getNewFillType()) !=
+        SkPathFillType_ConvertToNonInverse(pathB.getNewFillType())) {
         bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
         bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
         if (aCanChange != bCanChange) {
@@ -163,14 +163,14 @@
         REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
         REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
         if (ignoreInversenessDifference) {
-            pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
-            pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
+            pA.setFillType(SkPathFillType_ConvertToNonInverse(pathA.getNewFillType()));
+            pB.setFillType(SkPathFillType_ConvertToNonInverse(pathB.getNewFillType()));
         }
         if (ignoreWindingVsEvenOdd) {
-            pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
-                                                  : SkPath::kEvenOdd_FillType);
-            pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
-                                                  : SkPath::kEvenOdd_FillType);
+            pA.setFillType(pA.isInverseFillType() ? SkPathFillType::kInverseEvenOdd
+                                                  : SkPathFillType::kEvenOdd);
+            pB.setFillType(pB.isInverseFillType() ? SkPathFillType::kInverseEvenOdd
+                                                  : SkPathFillType::kEvenOdd);
         }
         if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
             REPORTER_ASSERT(r, keyA == keyB);
@@ -425,11 +425,11 @@
     PathGeo(const SkPath& path, Invert invert) : fPath(path)  {
         SkASSERT(!path.isInverseFillType());
         if (Invert::kYes == invert) {
-            if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
-                fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
+            if (fPath.getNewFillType() == SkPathFillType::kEvenOdd) {
+                fPath.setFillType(SkPathFillType::kInverseEvenOdd);
             } else {
-                SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
-                fPath.setFillType(SkPath::kInverseWinding_FillType);
+                SkASSERT(fPath.getNewFillType() == SkPathFillType::kWinding);
+                fPath.setFillType(SkPathFillType::kInverseWinding);
             }
         }
     }
@@ -457,7 +457,7 @@
         }
         SkRect rect;
         unsigned start;
-        SkPath::Direction dir;
+        SkPathDirection dir;
         if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
             return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
         }
@@ -572,7 +572,7 @@
         }
         // The bounds API explicitly calls out that it does not consider inverseness.
         SkPath p = path;
-        p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
+        p.setFillType(SkPathFillType_ConvertToNonInverse(path.getNewFillType()));
         REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
     }
 
@@ -1577,7 +1577,7 @@
     dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
                                         TestCase::kAllSame_ComparisonExpecation);
 
-    static constexpr SkPath::Direction kDir = SkPath::kCCW_Direction;
+    static constexpr SkPathDirection kDir = SkPathDirection::kCCW;
     static constexpr int kStart = 0;
 
     TestCase fillInvertedEmptyRRectCase(reporter, emptyRRect, kDir, kStart, true, GrStyle(fill));
@@ -1646,21 +1646,21 @@
     static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
 
     auto index = [](bool inverted,
-                    SkPath::Direction dir,
+                    SkPathDirection dir,
                     unsigned start,
                     Style style,
                     bool dash) -> int {
         return inverted * (2 * 8 * kStyleCnt * 2) +
-               dir      * (    8 * kStyleCnt * 2) +
+               (int)dir * (    8 * kStyleCnt * 2) +
                start    * (        kStyleCnt * 2) +
                style    * (                    2) +
                dash;
     };
-    static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
+    static const SkPathDirection kSecondDirection = static_cast<SkPathDirection>(1);
     const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
     SkAutoTArray<GrShape> shapes(cnt);
     for (bool inverted : {false, true}) {
-        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (SkPathDirection dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
             for (unsigned start = 0; start < 8; ++start) {
                 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
                     for (bool dash : {false, true}) {
@@ -1676,7 +1676,7 @@
 
     // Get the keys for some example shape instances that we'll use for comparision against the
     // rest.
-    static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
+    static constexpr SkPathDirection kExamplesDir = SkPathDirection::kCW;
     static constexpr unsigned kExamplesStart = 0;
     const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
                                                   false)];
@@ -1720,61 +1720,61 @@
 
     // These are dummy initializations to suppress warnings.
     SkRRect queryRR = SkRRect::MakeEmpty();
-    SkPath::Direction queryDir = SkPath::kCW_Direction;
+    SkPathDirection queryDir = SkPathDirection::kCW;
     unsigned queryStart = ~0U;
     bool queryInverted = true;
 
     REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, !queryInverted);
 
     REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                   &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, queryInverted);
 
     REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                         &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, !queryInverted);
 
     REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                            &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, queryInverted);
 
     REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                    &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, !queryInverted);
 
     REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                       &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, queryInverted);
 
     REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, !queryInverted);
 
     REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
                                                     &queryInverted));
     REPORTER_ASSERT(r, queryRR == rrect);
-    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
+    REPORTER_ASSERT(r, SkPathDirection::kCW == queryDir);
     REPORTER_ASSERT(r, 0 == queryStart);
     REPORTER_ASSERT(r, queryInverted);
 
@@ -1791,7 +1791,7 @@
     REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
 
     for (bool inverted : {false, true}) {
-        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (SkPathDirection dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
             for (unsigned start = 0; start < 8; ++start) {
                 for (bool dash : {false, true}) {
                     const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
@@ -1846,7 +1846,7 @@
 
                         // The pre-style case for the dash will match the non-dash example iff the
                         // dir and start match (dir=cw, start=0).
-                        if (0 == expectedStart && SkPath::kCW_Direction == dir) {
+                        if (0 == expectedStart && SkPathDirection::kCW == dir) {
                             e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
                             g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
                         } else {
@@ -1885,7 +1885,7 @@
     lineAC.lineTo(kC);
 
     SkPath invLineAB = lineAB;
-    invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
+    invLineAB.setFillType(SkPathFillType::kInverseEvenOdd);
 
     SkPaint fill;
     SkPaint stroke;
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 0b50d85..3a1026b 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -606,7 +606,7 @@
                         GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags ::kNone,
                         SkBackingFit::kExact, budgeted, GrProtected::kNo,
                         GrSurfaceProxy::UseAllocator::kYes);
-                rtc->drawTexture(GrNoClip(), proxy, GrColorType::kRGBA_8888,
+                rtc->drawTexture(GrNoClip(), proxy, GrColorType::kRGBA_8888, kPremul_SkAlphaType,
                                  GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
                                  SkPMColor4f(), SkRect::MakeWH(w, h), SkRect::MakeWH(w, h),
                                  GrAA::kNo, GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
@@ -621,10 +621,10 @@
 
                 // This time we move the proxy into the draw.
                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrColorType::kRGBA_8888,
-                                 GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
-                                 SkPMColor4f(), SkRect::MakeWH(w, h), SkRect::MakeWH(w, h),
-                                 GrAA::kNo, GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
-                                 SkMatrix::I(), nullptr);
+                                 kPremul_SkAlphaType, GrSamplerState::Filter::kNearest,
+                                 SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
+                                 SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
+                                 SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
                 context->flush();
                 context->priv().getGpu()->testingOnly_flushGpuAndSync();
@@ -667,7 +667,7 @@
                             auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
                                     texture, GrColorType::kRGBA_8888, kTopLeft_GrSurfaceOrigin);
                             rtc->drawTexture(
-                                    GrNoClip(), proxy, GrColorType::kRGBA_8888,
+                                    GrNoClip(), proxy, GrColorType::kRGBA_8888, kPremul_SkAlphaType,
                                     GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
                                     SkPMColor4f(), SkRect::MakeWH(w, h), SkRect::MakeWH(w, h),
                                     GrAA::kNo, GrQuadAAFlags::kNone,
diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp
index f4cebd3..e6469bb 100644
--- a/tests/ImageFilterTest.cpp
+++ b/tests/ImageFilterTest.cpp
@@ -503,6 +503,9 @@
     SkBitmap gradient = make_gradient_circle(kWidth, kHeight);
     sk_sp<SkSpecialImage> imgSrc(SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(kWidth, kHeight),
                                                                 gradient));
+    if (context) {
+        imgSrc = imgSrc->makeTextureImage(context);
+    }
 
     SkIPoint offset;
     SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr,
@@ -590,6 +593,9 @@
 
     sk_sp<SkSpecialImage> imgSrc(SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(kWidth, kHeight),
                                                                 bitmap));
+    if (context) {
+        imgSrc = imgSrc->makeTextureImage(context);
+    }
 
     SkIPoint offset;
     SkImageFilter_Base::Context ctx(SkMatrix::I(), SkIRect::MakeWH(32, 32), nullptr,
diff --git a/tests/MtlBackendAllocationTest.mm b/tests/MtlBackendAllocationTest.mm
index 2652bbd..f7ea5b5 100644
--- a/tests/MtlBackendAllocationTest.mm
+++ b/tests/MtlBackendAllocationTest.mm
@@ -114,16 +114,50 @@
                 }
 
                 {
-                    auto createWithColorMtd = [format](GrContext* context,
-                                                       const SkColor4f& color,
-                                                       GrMipMapped mipMapped,
-                                                       GrRenderable renderable) {
-                        return context->createBackendTexture(32, 32, format, color,
+                    // We're creating backend textures without specifying a color type "view" of
+                    // them at the public API level. Therefore, Ganesh will not apply any swizzles
+                    // before writing the color to the texture. However, our validation code does
+                    // rely on interpreting the texture contents via a SkColorType and therefore
+                    // swizzles may be applied during the read step.
+                    // Ideally we'd update our validation code to use a "raw" read that doesn't
+                    // impose a color type but for now we just munge the data we upload to match the
+                    // expectation.
+                    GrSwizzle swizzle;
+                    switch (combo.fColorType) {
+                        case GrColorType::kAlpha_8:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        case GrColorType::kAlpha_16:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        case GrColorType::kAlpha_F16:
+                            swizzle = GrSwizzle("aaaa");
+                            break;
+                        default:
+                            break;
+                    }
+                    auto createWithColorMtd = [format, swizzle](GrContext* context,
+                                                                const SkColor4f& color,
+                                                                GrMipMapped mipMapped,
+                                                                GrRenderable renderable) {
+                        auto swizzledColor = swizzle.applyTo(color);
+                        return context->createBackendTexture(32, 32, format, swizzledColor,
                                                              mipMapped, renderable);
                     };
-
-                    test_color_init(context, reporter, createWithColorMtd,
-                                    combo.fColorType, combo.fColor, mipMapped, renderable);
+                    // We make our comparison color using SkPixmap::erase(color) on a pixmap of
+                    // combo.fColorType and then calling SkPixmap::readPixels(). erase() will premul
+                    // the color passed to it. However, createBackendTexture() that takes a
+                    // SkColor4f is color type/alpha type unaware and will simply compute luminance
+                    // from the r, g, b, channels.
+                    SkColor4f color = combo.fColor;
+                    if (combo.fColorType == GrColorType::kGray_8) {
+                        color = {color.fR * color.fA,
+                            color.fG * color.fA,
+                            color.fB * color.fA,
+                            1.f};
+                    }
+                    test_color_init(context, reporter, createWithColorMtd, combo.fColorType, color,
+                                    mipMapped, renderable);
                 }
             }
         }
diff --git a/tests/MtlCopySurfaceTest.mm b/tests/MtlCopySurfaceTest.mm
new file mode 100644
index 0000000..cc89782
--- /dev/null
+++ b/tests/MtlCopySurfaceTest.mm
@@ -0,0 +1,74 @@
+/*
+ * 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 "include/core/SkSurface.h"
+#include "include/gpu/GrContext.h"
+#include "src/gpu/GrContextPriv.h"
+#include "src/gpu/mtl/GrMtlGpu.h"
+#include "tests/Test.h"
+
+#import <Metal/Metal.h>
+#import <MetalKit/MTKView.h>
+
+#include "src/gpu/mtl/GrMtlCaps.h"
+#include "src/gpu/mtl/GrMtlTextureRenderTarget.h"
+
+DEF_GPUTEST_FOR_METAL_CONTEXT(MtlCopySurfaceTest, reporter, ctxInfo) {
+    static const int kWidth = 1024;
+    static const int kHeight = 768;
+
+    GrContext* context = ctxInfo.grContext();
+
+    // This is a bit weird, but it's the only way to get a framebufferOnly surface
+    GrMtlGpu* gpu = (GrMtlGpu*) context->priv().getGpu();
+
+    MTKView* view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)
+                                            device:gpu->device()];
+    id<CAMetalDrawable> drawable = [view currentDrawable];
+    REPORTER_ASSERT(reporter, drawable.texture.framebufferOnly);
+    REPORTER_ASSERT(reporter, drawable.texture.usage & MTLTextureUsageRenderTarget);
+
+    // Test to see if we can initiate a copy via GrSurfaceProxys
+    SkSurfaceProps props(0, kRGB_H_SkPixelGeometry);
+
+    // TODO: check multisampled RT as well
+    GrMtlTextureInfo fbInfo;
+    fbInfo.fTexture.retain((__bridge const void*)(drawable.texture));
+    GrBackendRenderTarget backendRT(kWidth, kHeight, 1, fbInfo);
+
+    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
+    sk_sp<GrSurfaceProxy> srcProxy = proxyProvider->wrapBackendRenderTarget(
+                                             backendRT, GrColorType::kBGRA_8888,
+                                             kTopLeft_GrSurfaceOrigin);
+
+    sk_sp<GrTextureProxy> dstProxy = GrSurfaceProxy::Copy(context, srcProxy.get(),
+                                                          GrMipMapped::kNo,
+                                                          SkBackingFit::kExact,
+                                                          SkBudgeted::kYes);
+
+    // TODO: GrSurfaceProxy::Copy doesn't check to see if the framebufferOnly bit is set yet.
+    // Update this when it does -- it should fail.
+    if (!dstProxy) {
+        ERRORF(reporter, "Expected copy to succeed");
+    }
+
+    // Try direct copy via GPU (should fail)
+    GrSurfaceDesc desc;
+    desc.fWidth = kWidth;
+    desc.fHeight = kHeight;
+    GrBackendFormat backendFormat = GrBackendFormat::MakeMtl(drawable.texture.pixelFormat);
+    desc.fConfig = gpu->caps()->getConfigFromBackendFormat(backendFormat,
+                                                           GrColorType::kBGRA_8888);
+    GrSurface* src = srcProxy->peekSurface();
+    sk_sp<GrTexture> dst = gpu->createTexture(desc, backendFormat, GrRenderable::kNo,
+                                              1, GrMipMapped::kNo, SkBudgeted::kNo,
+                                              GrProtected::kNo);
+
+    bool result = gpu->copySurface(dst.get(), src, SkIRect::MakeXYWH(0, 0, kWidth, kHeight),
+                                   SkIPoint::Make(0, 0));
+    REPORTER_ASSERT(reporter, !result);
+}
diff --git a/tests/MultiPictureDocumentTest.cpp b/tests/MultiPictureDocumentTest.cpp
index 28830c0..5d422ed 100644
--- a/tests/MultiPictureDocumentTest.cpp
+++ b/tests/MultiPictureDocumentTest.cpp
@@ -170,7 +170,7 @@
     std::unique_ptr<SkStreamAsset> writtenStream = stream.detachAsStream();
     REPORTER_ASSERT(reporter, writtenStream->getLength() > 24,
         "Written data length too short (%d)", writtenStream->getLength());
-    SkDebugf("Multi Frame file size = %d\n", writtenStream->getLength());
+    // SkDebugf("Multi Frame file size = %d\n", writtenStream->getLength());
 
     // Set up deserialization
     SkSharingDeserialContext deserialContext;
diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp
index 6222f16..62a61bb 100644
--- a/tests/OnFlushCallbackTest.cpp
+++ b/tests/OnFlushCallbackTest.cpp
@@ -99,8 +99,9 @@
         static const int kColorOffset = sizeof(SkPoint);
         static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
 
-        sk_sp<GrGeometryProcessor> gp =
-                GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+        GrGeometryProcessor* gp = GrDefaultGeoProcFactory::Make(
+                                              target->allocator(),
+                                              target->caps().shaderCaps(),
                                               Color::kPremulGrColorAttribute_Type,
                                               Coverage::kSolid_Type,
                                               fHasLocalRect ? LocalCoords::kHasExplicit_Type
@@ -161,7 +162,7 @@
         mesh->setIndexed(indexBuffer, 6, firstIndex, 0, 3, GrPrimitiveRestart::kNo);
         mesh->setVertexData(vertexBuffer, firstVertex);
 
-        target->recordDraw(std::move(gp), mesh);
+        target->recordDraw(gp, mesh, 1, GrPrimitiveType::kTriangles);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
@@ -434,7 +435,7 @@
 // This creates an off-screen rendertarget whose ops which eventually pull from the atlas.
 static sk_sp<GrTextureProxy> make_upstream_image(GrContext* context, AtlasObject* object, int start,
                                                  sk_sp<GrTextureProxy> atlasProxy,
-                                                 GrColorType atlasColorType) {
+                                                 SkAlphaType atlasAlphaType) {
     auto rtc = context->priv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
                                                                3* kDrawnTileSize,
                                                                kDrawnTileSize,
@@ -446,7 +447,7 @@
     for (int i = 0; i < 3; ++i) {
         SkRect r = SkRect::MakeXYWH(i*kDrawnTileSize, 0, kDrawnTileSize, kDrawnTileSize);
 
-        auto fp = GrSimpleTextureEffect::Make(atlasProxy, atlasColorType, SkMatrix::I());
+        auto fp = GrSimpleTextureEffect::Make(atlasProxy, atlasAlphaType, SkMatrix::I());
         GrPaint paint;
         paint.addColorFragmentProcessor(std::move(fp));
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
@@ -544,10 +545,9 @@
 
     sk_sp<GrTextureProxy> proxies[kNumProxies];
     for (int i = 0; i < kNumProxies; ++i) {
-        proxies[i] = make_upstream_image(context, &object, i*3,
-                                         object.getAtlasProxy(proxyProvider,
-                                                              context->priv().caps()),
-                                         GrColorType::kRGBA_8888);
+        proxies[i] = make_upstream_image(
+                context, &object, i*3,
+                object.getAtlasProxy(proxyProvider, context->priv().caps()), kPremul_SkAlphaType);
     }
 
     static const int kFinalWidth = 6*kDrawnTileSize;
@@ -565,7 +565,7 @@
         SkMatrix t = SkMatrix::MakeTrans(-i*3*kDrawnTileSize, 0);
 
         GrPaint paint;
-        auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), GrColorType::kRGBA_8888, t);
+        auto fp = GrSimpleTextureEffect::Make(std::move(proxies[i]), kPremul_SkAlphaType, t);
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
         paint.addColorFragmentProcessor(std::move(fp));
 
diff --git a/tests/OpChainTest.cpp b/tests/OpChainTest.cpp
index c662843..b2560a1 100644
--- a/tests/OpChainTest.cpp
+++ b/tests/OpChainTest.cpp
@@ -171,11 +171,16 @@
         context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
                                                         GrRenderable::kYes);
 
+    static const GrSurfaceOrigin kOrigin = kTopLeft_GrSurfaceOrigin;
     auto proxy = context->priv().proxyProvider()->createProxy(
-            format, desc, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
+            format, desc, GrRenderable::kYes, 1, kOrigin, GrMipMapped::kNo,
             SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone);
     SkASSERT(proxy);
     proxy->instantiate(context->priv().resourceProvider());
+
+    GrSwizzle outSwizzle = context->priv().caps()->getOutputSwizzle(format,
+                                                                    GrColorType::kRGBA_8888);
+
     int result[result_width()];
     int validResult[result_width()];
 
@@ -206,7 +211,7 @@
                                           context->priv().resourceProvider(),
                                           &tracker);
                 GrOpsTask opsTask(context->priv().refOpMemoryPool(),
-                                  sk_ref_sp(proxy->asRenderTargetProxy()),
+                                  GrSurfaceProxyView(proxy, kOrigin, outSwizzle),
                                   context->priv().auditTrail());
                 // This assumes the particular values of kRanges.
                 std::fill_n(result, result_width(), -1);
diff --git a/tests/PathOpsAsWindingTest.cpp b/tests/PathOpsAsWindingTest.cpp
index e92dc0d..327b633 100644
--- a/tests/PathOpsAsWindingTest.cpp
+++ b/tests/PathOpsAsWindingTest.cpp
@@ -9,9 +9,9 @@
 #include "tests/PathOpsThreadedCommon.h"
 #include "tests/Test.h"
 
-static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPath::Direction dir) {
+static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPathDirection dir) {
     SkPath path;
-    bool reverse = SkPath::kCCW_Direction == dir;
+    bool reverse = SkPathDirection::kCCW == dir;
     switch (verb) {
         case SkPath::kLine_Verb:
             path.addRect(rect, dir);
@@ -58,58 +58,58 @@
     REPORTER_ASSERT(reporter, test == result);
     // if test is empty
     test.reset();
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, result.isEmpty());
-    REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
+    REPORTER_ASSERT(reporter, result.getNewFillType() == SkPathFillType::kWinding);
     // if test is convex
     test.addCircle(5, 5, 10);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, result.isConvex());
-    test.setFillType(SkPath::kWinding_FillType);
+    test.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, test == result);
     // if test has infinity
     test.reset();
     test.addRect({1, 2, 3, SK_ScalarInfinity});
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, !AsWinding(test, &result));
     // if test has only one contour
     test.reset();
     SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
     test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, !result.isConvex());
-    test.setFillType(SkPath::kWinding_FillType);
+    test.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, test == result);
     // test two contours that do not overlap or share bounds
     test.addRect({5, 2, 6, 3});
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, !result.isConvex());
-    test.setFillType(SkPath::kWinding_FillType);
+    test.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, test == result);
     // test two contours that do not overlap but share bounds
     test.reset();
     test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
     test.addRect({2, 2, 3, 3});
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, !result.isConvex());
-    test.setFillType(SkPath::kWinding_FillType);
+    test.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, test == result);
     // test two contours that partially overlap
     test.reset();
     test.addRect({0, 0, 3, 3});
     test.addRect({1, 1, 4, 4});
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     REPORTER_ASSERT(reporter, !result.isConvex());
-    test.setFillType(SkPath::kWinding_FillType);
+    test.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, test == result);
     // test that result may be input
     SkPath copy = test;
-    test.setFillType(SkPath::kEvenOdd_FillType);
+    test.setFillType(SkPathFillType::kEvenOdd);
     REPORTER_ASSERT(reporter, AsWinding(test, &test));
     REPORTER_ASSERT(reporter, !test.isConvex());
     REPORTER_ASSERT(reporter, test == copy);
@@ -119,10 +119,10 @@
     const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
     const std::initializer_list<SkPoint> revBcw  = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
     for (bool aFirst : {false, true}) {
-        for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
-            for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
+            for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
                 test.reset();
-                test.setFillType(SkPath::kEvenOdd_FillType);
+                test.setFillType(SkPathFillType::kEvenOdd);
                 if (aFirst) {
                     test.addRect(rectA, dirA);
                     test.addRect(rectB, dirB);
@@ -132,7 +132,7 @@
                 }
                 SkPath original = test;
                 REPORTER_ASSERT(reporter, AsWinding(test, &result));
-                REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
+                REPORTER_ASSERT(reporter, result.getNewFillType() == SkPathFillType::kWinding);
                 test.reset();
                 if (aFirst) {
                     test.addRect(rectA, dirA);
@@ -140,7 +140,7 @@
                 if (dirA != dirB) {
                     test.addRect(rectB, dirB);
                 } else {
-                    test.addPoly(SkPath::kCW_Direction == dirA ? revBccw : revBcw, true);
+                    test.addPoly(SkPathDirection::kCW == dirA ? revBccw : revBcw, true);
                 }
                 if (!aFirst) {
                     test.addRect(rectA, dirA);
@@ -148,7 +148,7 @@
                 REPORTER_ASSERT(reporter, test == result);
                 // test that result may be input
                 REPORTER_ASSERT(reporter, AsWinding(original, &original));
-                REPORTER_ASSERT(reporter, original.getFillType() == SkPath::kWinding_FillType);
+                REPORTER_ASSERT(reporter, original.getNewFillType() == SkPathFillType::kWinding);
                 REPORTER_ASSERT(reporter, original == result);
             }
         }
@@ -156,8 +156,8 @@
     // Test curve types with donuts. Create a donut with outer and hole in all directions.
     // After converting to winding, all donuts should have a hole in the middle.
     for (bool aFirst : {false, true}) {
-        for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
-            for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+        for (auto dirA : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
+            for (auto dirB : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
                 for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
                                      SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
                     SkPath pathA = build_squircle(curveA, rectA, dirA);
@@ -168,9 +168,9 @@
                         if (!aFirst) {
                             test.addPath(pathA);
                         }
-                        test.setFillType(SkPath::kEvenOdd_FillType);
+                        test.setFillType(SkPathFillType::kEvenOdd);
                         REPORTER_ASSERT(reporter, AsWinding(test, &result));
-                       REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
+                       REPORTER_ASSERT(reporter, result.getNewFillType() == SkPathFillType::kWinding);
                         for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
                             for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
                                 bool evenOddContains = test.contains(x, y);
diff --git a/tests/PathOpsBattles.cpp b/tests/PathOpsBattles.cpp
index f3c5140..6f4bc95 100644
--- a/tests/PathOpsBattles.cpp
+++ b/tests/PathOpsBattles.cpp
@@ -42,7 +42,7 @@
 static void issue414409b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1, path2;
     // one fill=0 op=2
-path1.setFillType((SkPath::FillType) 0);
+path1.setFillType((SkPathFillType) 0);
 path1.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path1.cubicTo(SkBits2Float(0x41f12edc), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4267b362), SkBits2Float(0xc2854e1f), SkBits2Float(0x42911faa), SkBits2Float(0xc2212f3b));
 path1.cubicTo(SkBits2Float(0x42ae65a2), SkBits2Float(0xc15f08de), SkBits2Float(0x42acc913), SkBits2Float(0x41923f59), SkBits2Float(0x428ce9f0), SkBits2Float(0x422f7dc4));
@@ -52,7 +52,7 @@
 path1.lineTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path1.close();
 
-path2.setFillType((SkPath::FillType) 1);
+path2.setFillType((SkPathFillType) 1);
 path2.moveTo(SkBits2Float(0x428ce9ef), SkBits2Float(0x422f7dc6));
 path2.cubicTo(SkBits2Float(0x4286af43), SkBits2Float(0x42437fa7), SkBits2Float(0x427ed0d6), SkBits2Float(0x42561f5a), SkBits2Float(0x426e69d2), SkBits2Float(0x42670c39));
 path2.lineTo(SkBits2Float(0x422c58d6), SkBits2Float(0x422705c1));
@@ -64,7 +64,7 @@
 
 static void issue414409c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1, path2;
-path1.setFillType((SkPath::FillType) 1);
+path1.setFillType((SkPathFillType) 1);
 path1.moveTo(SkBits2Float(0x36961ef0), SkBits2Float(0xc2700000));
 path1.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path1.cubicTo(SkBits2Float(0x3df86648), SkBits2Float(0xc2a60000), SkBits2Float(0x3e786777), SkBits2Float(0xc2a5ffdc), SkBits2Float(0x3eba4dc2), SkBits2Float(0xc2a5ff96));
@@ -76,7 +76,7 @@
 path1.cubicTo(SkBits2Float(0x3e3391e9), SkBits2Float(0xc26fffce), SkBits2Float(0x3db3931e), SkBits2Float(0xc2700000), SkBits2Float(0x36961ef0), SkBits2Float(0xc2700000));
 path1.close();
 
-path2.setFillType((SkPath::FillType) 0);
+path2.setFillType((SkPathFillType) 0);
 path2.moveTo(SkBits2Float(0x3eccef1a), SkBits2Float(0xc2a5ff81));
 path2.cubicTo(SkBits2Float(0x3f18c8a9), SkBits2Float(0xc2a5ff04), SkBits2Float(0x3f4b19b0), SkBits2Float(0xc2a5fe2d), SkBits2Float(0x3f7d6a37), SkBits2Float(0xc2a5fcfa));
 path2.lineTo(SkBits2Float(0x3f3730f2), SkBits2Float(0xc26ffba1));
@@ -90,7 +90,7 @@
 // fails to draw correctly
 static void battleOp1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ea4d9f5), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f24d9a9), SkBits2Float(0xc2a5ff0a), SkBits2Float(0x3f774519), SkBits2Float(0xc2a5fd1f));
 path.lineTo(SkBits2Float(0x3f32bfc3), SkBits2Float(0xc26ffbd7));
@@ -100,7 +100,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f774503), SkBits2Float(0xc2a5fd1f));
 path.cubicTo(SkBits2Float(0x3f7f82ff), SkBits2Float(0xc2a5fcee), SkBits2Float(0x3f83e06d), SkBits2Float(0xc2a5fcbb), SkBits2Float(0x3f87ff59), SkBits2Float(0xc2a5fc85));
 path.lineTo(SkBits2Float(0x3f449f80), SkBits2Float(0xc26ffaf7));
@@ -115,14 +115,14 @@
 
 static void battleOp2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ea4d9e6), SkBits2Float(0xc2a60000), SkBits2Float(0x3f24d99a), SkBits2Float(0xc2a5ff0a), SkBits2Float(0x3f774503), SkBits2Float(0xc2a5fd1f));
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f87ff64), SkBits2Float(0xc2a5fc85));
 path.cubicTo(SkBits2Float(0x3fcac720), SkBits2Float(0xc2a5f91a), SkBits2Float(0x4006c62a), SkBits2Float(0xc2a5f329), SkBits2Float(0x40282667), SkBits2Float(0xc2a5eab4));
 path.lineTo(SkBits2Float(0x3ff31bb9), SkBits2Float(0xc26fe136));
@@ -137,7 +137,7 @@
 
 static void battleOp3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f19f03c), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f99ef95), SkBits2Float(0xc2a5fca7), SkBits2Float(0x3fe6e2fa), SkBits2Float(0xc2a5f5f7));
 path.lineTo(SkBits2Float(0x3fa6e80c), SkBits2Float(0xc26ff17d));
@@ -147,7 +147,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fe6e322), SkBits2Float(0xc2a5f5f7));
 path.cubicTo(SkBits2Float(0x3fee94fb), SkBits2Float(0xc2a5f54c), SkBits2Float(0x3ff646db), SkBits2Float(0xc2a5f497), SkBits2Float(0x3ffdf8ad), SkBits2Float(0xc2a5f3db));
 path.lineTo(SkBits2Float(0x3fb79813), SkBits2Float(0xc26fee71));
@@ -162,7 +162,7 @@
 
 static void battleOp4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f19f03c), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f99ef95), SkBits2Float(0xc2a5fca7), SkBits2Float(0x3fe6e322), SkBits2Float(0xc2a5f5f7));
@@ -175,7 +175,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ffdf8c6), SkBits2Float(0xc2a5f3db));
 path.cubicTo(SkBits2Float(0x403d5556), SkBits2Float(0xc2a5e7ed), SkBits2Float(0x407ba65a), SkBits2Float(0xc2a5d338), SkBits2Float(0x409cf3fe), SkBits2Float(0xc2a5b5bc));
 path.lineTo(SkBits2Float(0x4062eb8a), SkBits2Float(0xc26f94a1));
@@ -190,7 +190,7 @@
 
 static void battleOp5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fe06a9b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40606368), SkBits2Float(0xc2a5e38e), SkBits2Float(0x40a82f8a), SkBits2Float(0xc2a5aab6));
 path.lineTo(SkBits2Float(0x40732902), SkBits2Float(0xc26f84b2));
@@ -200,7 +200,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40a82f91), SkBits2Float(0xc2a5aab7));
 path.cubicTo(SkBits2Float(0x40adc8dc), SkBits2Float(0xc2a5a508), SkBits2Float(0x40b361d8), SkBits2Float(0xc2a59f10), SkBits2Float(0x40b8fa82), SkBits2Float(0xc2a598d0));
 path.lineTo(SkBits2Float(0x4085b825), SkBits2Float(0xc26f6ad0));
@@ -215,7 +215,7 @@
 
 static void battleOp6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fe06a9b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40606368), SkBits2Float(0xc2a5e38e), SkBits2Float(0x40a82f91), SkBits2Float(0xc2a5aab7));
@@ -232,7 +232,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40b8fa77), SkBits2Float(0xc2a598d0));
 path.cubicTo(SkBits2Float(0x4109d7e9), SkBits2Float(0xc2a5337c), SkBits2Float(0x4137014a), SkBits2Float(0xc2a483b2), SkBits2Float(0x4163cbb6), SkBits2Float(0xc2a38a24));
 path.lineTo(SkBits2Float(0x4124abf0), SkBits2Float(0xc26c715c));
@@ -246,7 +246,7 @@
 
 static void battleOp7(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3de5c884), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e65c882), SkBits2Float(0xc2a5ffe2), SkBits2Float(0x3eac5645), SkBits2Float(0xc2a5ffa7));
 path.lineTo(SkBits2Float(0x3e79297e), SkBits2Float(0xc26fff7f));
@@ -256,7 +256,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3eac564d), SkBits2Float(0xc2a5ffa7));
 path.cubicTo(SkBits2Float(0x3eb21458), SkBits2Float(0xc2a5ffa1), SkBits2Float(0x3eb7d2fc), SkBits2Float(0xc2a5ff9b), SkBits2Float(0x3ebd91a0), SkBits2Float(0xc2a5ff94));
 path.lineTo(SkBits2Float(0x3e8909ff), SkBits2Float(0xc26fff64));
@@ -271,7 +271,7 @@
 
 static void battleOp8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3de5c884), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e65c882), SkBits2Float(0xc2a5ffe2), SkBits2Float(0x3eac564d), SkBits2Float(0xc2a5ffa7));
@@ -283,7 +283,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ebd921a), SkBits2Float(0xc2a5ff94));
 path.cubicTo(SkBits2Float(0x3f0d545f), SkBits2Float(0xc2a5ff29), SkBits2Float(0x3f3bdfbd), SkBits2Float(0xc2a5fe71), SkBits2Float(0x3f6a6ab6), SkBits2Float(0xc2a5fd69));
 path.lineTo(SkBits2Float(0x3f297558), SkBits2Float(0xc26ffc43));
@@ -298,7 +298,7 @@
 
 static void battleOp9(skiatest::Reporter* reporter, const char* filename) { // crashes
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ecc43bf), SkBits2Float(0xc2a60000), SkBits2Float(0x3f4c4385), SkBits2Float(0xc2a5fe87), SkBits2Float(0x3f993163), SkBits2Float(0xc2a5fb95));
 path.lineTo(SkBits2Float(0x3f5d7bc4), SkBits2Float(0xc26ff99d));
@@ -308,7 +308,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f993156), SkBits2Float(0xc2a5fb95));
 path.cubicTo(SkBits2Float(0x3f9e4c7a), SkBits2Float(0xc2a5fb49), SkBits2Float(0x3fa36794), SkBits2Float(0xc2a5fafa), SkBits2Float(0x3fa882aa), SkBits2Float(0xc2a5faa7));
 path.lineTo(SkBits2Float(0x3f73a149), SkBits2Float(0xc26ff845));
@@ -322,7 +322,7 @@
 
 static void battleOp10(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ddcd524), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e5cd462), SkBits2Float(0xc2a5ffe3), SkBits2Float(0x3ea59eff), SkBits2Float(0xc2a5ffac));
 path.lineTo(SkBits2Float(0x3e6f74a3), SkBits2Float(0xc26fff89));
@@ -332,7 +332,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ea59f9c), SkBits2Float(0xc2a5ffad));
 path.cubicTo(SkBits2Float(0x3eab24c0), SkBits2Float(0xc2a5ffa7), SkBits2Float(0x3eb0aa54), SkBits2Float(0xc2a5ffa1), SkBits2Float(0x3eb62fe9), SkBits2Float(0xc2a5ff9b));
 path.lineTo(SkBits2Float(0x3e83b355), SkBits2Float(0xc26fff6f));
@@ -347,7 +347,7 @@
 
 static void battleOp11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ddcd524), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e5cd462), SkBits2Float(0xc2a5ffe3), SkBits2Float(0x3ea59f9c), SkBits2Float(0xc2a5ffad));
@@ -364,7 +364,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3eb62f8c), SkBits2Float(0xc2a5ff9c));
 path.cubicTo(SkBits2Float(0x3f07d31d), SkBits2Float(0xc2a5ff3a), SkBits2Float(0x3f348e3e), SkBits2Float(0xc2a5fe8f), SkBits2Float(0x3f614904), SkBits2Float(0xc2a5fd9c));
 path.lineTo(SkBits2Float(0x3f22db6c), SkBits2Float(0xc26ffc8c));
@@ -379,7 +379,7 @@
 
 static void battleOp12(skiatest::Reporter* reporter, const char* filename) {  // crashed
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ecc43bf), SkBits2Float(0xc2a60000), SkBits2Float(0x3f4c4385), SkBits2Float(0xc2a5fe87), SkBits2Float(0x3f993163), SkBits2Float(0xc2a5fb95));
 path.lineTo(SkBits2Float(0x3f5d7bc4), SkBits2Float(0xc26ff99d));
@@ -389,7 +389,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f993156), SkBits2Float(0xc2a5fb95));
 path.cubicTo(SkBits2Float(0x3f9e4c7a), SkBits2Float(0xc2a5fb49), SkBits2Float(0x3fa36794), SkBits2Float(0xc2a5fafa), SkBits2Float(0x3fa882aa), SkBits2Float(0xc2a5faa7));
 path.lineTo(SkBits2Float(0x3f73a149), SkBits2Float(0xc26ff845));
@@ -404,7 +404,7 @@
 
 static void battleOp13(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ddcd524), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e5cd462), SkBits2Float(0xc2a5ffe3), SkBits2Float(0x3ea59eff), SkBits2Float(0xc2a5ffac));
 path.lineTo(SkBits2Float(0x3e6f74a3), SkBits2Float(0xc26fff89));
@@ -414,7 +414,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ea59f9c), SkBits2Float(0xc2a5ffad));
 path.cubicTo(SkBits2Float(0x3eab24c0), SkBits2Float(0xc2a5ffa7), SkBits2Float(0x3eb0aa54), SkBits2Float(0xc2a5ffa1), SkBits2Float(0x3eb62fe9), SkBits2Float(0xc2a5ff9b));
 path.lineTo(SkBits2Float(0x3e83b355), SkBits2Float(0xc26fff6f));
@@ -429,7 +429,7 @@
 
 static void battleOp14(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ddcd524), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e5cd462), SkBits2Float(0xc2a5ffe3), SkBits2Float(0x3ea59f9c), SkBits2Float(0xc2a5ffad));
@@ -446,7 +446,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3eb62f8c), SkBits2Float(0xc2a5ff9c));
 path.cubicTo(SkBits2Float(0x3f07d31d), SkBits2Float(0xc2a5ff3a), SkBits2Float(0x3f348e3e), SkBits2Float(0xc2a5fe8f), SkBits2Float(0x3f614904), SkBits2Float(0xc2a5fd9c));
 path.lineTo(SkBits2Float(0x3f22db6c), SkBits2Float(0xc26ffc8c));
@@ -461,7 +461,7 @@
 
 static void battleOp15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f19f03c), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f99ef95), SkBits2Float(0xc2a5fca7), SkBits2Float(0x3fe6e2fa), SkBits2Float(0xc2a5f5f7));
 path.lineTo(SkBits2Float(0x3fa6e80c), SkBits2Float(0xc26ff17d));
@@ -471,7 +471,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fe6e322), SkBits2Float(0xc2a5f5f7));
 path.cubicTo(SkBits2Float(0x3fee94fb), SkBits2Float(0xc2a5f54c), SkBits2Float(0x3ff646db), SkBits2Float(0xc2a5f497), SkBits2Float(0x3ffdf8ad), SkBits2Float(0xc2a5f3db));
 path.lineTo(SkBits2Float(0x3fb79813), SkBits2Float(0xc26fee71));
@@ -486,7 +486,7 @@
 
 static void battleOp16(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f19f03c), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f99ef95), SkBits2Float(0xc2a5fca7), SkBits2Float(0x3fe6e322), SkBits2Float(0xc2a5f5f7));
@@ -499,7 +499,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ffdf8c6), SkBits2Float(0xc2a5f3db));
 path.cubicTo(SkBits2Float(0x403d5556), SkBits2Float(0xc2a5e7ed), SkBits2Float(0x407ba65a), SkBits2Float(0xc2a5d338), SkBits2Float(0x409cf3fe), SkBits2Float(0xc2a5b5bc));
 path.lineTo(SkBits2Float(0x4062eb8a), SkBits2Float(0xc26f94a1));
@@ -514,7 +514,7 @@
 
 static void battleOp17(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f9860dc), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40185ea2), SkBits2Float(0xc2a5f2e2), SkBits2Float(0x40647d09), SkBits2Float(0xc2a5d8aa));
 path.lineTo(SkBits2Float(0x40252c2a), SkBits2Float(0xc26fc723));
@@ -524,7 +524,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40647d17), SkBits2Float(0xc2a5d8ab));
 path.cubicTo(SkBits2Float(0x406c19ae), SkBits2Float(0xc2a5d60b), SkBits2Float(0x4073b608), SkBits2Float(0xc2a5d34a), SkBits2Float(0x407b5230), SkBits2Float(0xc2a5d069));
 path.lineTo(SkBits2Float(0x4035ad90), SkBits2Float(0xc26fbb32));
@@ -539,7 +539,7 @@
 
 static void battleOp18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3664fea3), SkBits2Float(0xc26ffffe));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f9860dc), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40185ea2), SkBits2Float(0xc2a5f2e2), SkBits2Float(0x40647d17), SkBits2Float(0xc2a5d8ab));
@@ -551,7 +551,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x407b523a), SkBits2Float(0xc2a5d069));
 path.cubicTo(SkBits2Float(0x40bb53e8), SkBits2Float(0xc2a5a1ad), SkBits2Float(0x40f8dfd1), SkBits2Float(0xc2a5508e), SkBits2Float(0x411b1813), SkBits2Float(0xc2a4dd32));
 path.lineTo(SkBits2Float(0x40e03b7c), SkBits2Float(0xc26e5b8f));
@@ -566,7 +566,7 @@
 
 static void battleOp19(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40272e66), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40a7227d), SkBits2Float(0xc2a5c0db), SkBits2Float(0x40fa5a70), SkBits2Float(0xc2a542ca));
 path.lineTo(SkBits2Float(0x40b4fa6e), SkBits2Float(0xc26eee73));
@@ -576,7 +576,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40fa5a6d), SkBits2Float(0xc2a542cb));
 path.cubicTo(SkBits2Float(0x4101563b), SkBits2Float(0xc2a5362f), SkBits2Float(0x41057ec0), SkBits2Float(0xc2a528f4), SkBits2Float(0x4109a6c0), SkBits2Float(0xc2a51b18));
 path.lineTo(SkBits2Float(0x40c70391), SkBits2Float(0xc26eb50e));
@@ -591,7 +591,7 @@
 
 static void battleOp20(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40272e63), SkBits2Float(0xc2a60000), SkBits2Float(0x40a7227a), SkBits2Float(0xc2a5c0db), SkBits2Float(0x40fa5a6c), SkBits2Float(0xc2a542ca));
@@ -604,7 +604,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4109a6bc), SkBits2Float(0xc2a51b19));
 path.cubicTo(SkBits2Float(0x414d093d), SkBits2Float(0xc2a43a61), SkBits2Float(0x4187e474), SkBits2Float(0xc2a2b4fa), SkBits2Float(0x41a8a805), SkBits2Float(0xc2a08e4d));
 path.lineTo(SkBits2Float(0x4173d72c), SkBits2Float(0xc2682105));
@@ -619,7 +619,7 @@
 
 static void battleOp21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x404ef9c5), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40cee321), SkBits2Float(0xc2a59f3a), SkBits2Float(0x411ad5ab), SkBits2Float(0xc2a4de2c));
 path.lineTo(SkBits2Float(0x40dfdb77), SkBits2Float(0xc26e5cf8));
@@ -629,7 +629,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x411ad5aa), SkBits2Float(0xc2a4de2c));
 path.cubicTo(SkBits2Float(0x411ff8ea), SkBits2Float(0xc2a4cadf), SkBits2Float(0x41251b3e), SkBits2Float(0xc2a4b69c), SkBits2Float(0x412a3c98), SkBits2Float(0xc2a4a163));
 path.lineTo(SkBits2Float(0x40f6200f), SkBits2Float(0xc26e0518));
@@ -644,7 +644,7 @@
 
 static void battleOp22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x407fb41a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40ff895b), SkBits2Float(0xc2a56c4b), SkBits2Float(0x413f077c), SkBits2Float(0xc2a44609));
 path.lineTo(SkBits2Float(0x410a17ee), SkBits2Float(0xc26d8104));
@@ -654,7 +654,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x413f0780), SkBits2Float(0xc2a44609));
 path.cubicTo(SkBits2Float(0x41455a4a), SkBits2Float(0xc2a4289f), SkBits2Float(0x414bab5a), SkBits2Float(0xc2a409bf), SkBits2Float(0x4151fa92), SkBits2Float(0xc2a3e96b));
 path.lineTo(SkBits2Float(0x4117cabb), SkBits2Float(0xc26cfb1d));
@@ -669,7 +669,7 @@
 
 static void battleOp23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x407fb41a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40ff895b), SkBits2Float(0xc2a56c4b), SkBits2Float(0x413f0780), SkBits2Float(0xc2a44609));
@@ -681,7 +681,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4151fa93), SkBits2Float(0xc2a3e96b));
 path.cubicTo(SkBits2Float(0x419c2b7d), SkBits2Float(0xc2a1dce5), SkBits2Float(0x41ce36f8), SkBits2Float(0xc29e52a6), SkBits2Float(0x41fe1a0a), SkBits2Float(0xc2995d2e));
 path.lineTo(SkBits2Float(0x41b7b024), SkBits2Float(0xc25dbb29));
@@ -696,7 +696,7 @@
 
 static void battleOp24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x409bc7b0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x411ba103), SkBits2Float(0xc2a524b6), SkBits2Float(0x4168515c), SkBits2Float(0xc2a370af));
 path.lineTo(SkBits2Float(0x4127f0cc), SkBits2Float(0xc26c4c8f));
@@ -706,7 +706,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4168515e), SkBits2Float(0xc2a370b0));
 path.cubicTo(SkBits2Float(0x416ffb5b), SkBits2Float(0xc2a3451c), SkBits2Float(0x4177a23d), SkBits2Float(0xc2a31761), SkBits2Float(0x417f45ca), SkBits2Float(0xc2a2e77f));
 path.lineTo(SkBits2Float(0x413888ce), SkBits2Float(0xc26b8638));
@@ -721,7 +721,7 @@
 
 static void battleOp25(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3655fea5), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x409bc7b0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x411ba103), SkBits2Float(0xc2a524b6), SkBits2Float(0x4168515e), SkBits2Float(0xc2a370b0));
@@ -733,7 +733,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x417f45c8), SkBits2Float(0xc2a2e780));
 path.cubicTo(SkBits2Float(0x41bda27d), SkBits2Float(0xc29fde49), SkBits2Float(0x41f99531), SkBits2Float(0xc29aa2c4), SkBits2Float(0x4218d569), SkBits2Float(0xc2935d77));
 path.lineTo(SkBits2Float(0x41dcf6db), SkBits2Float(0xc2550ed7));
@@ -748,7 +748,7 @@
 
 static void battleOp26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40b98c15), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41394aaf), SkBits2Float(0xc2a4c8e8), SkBits2Float(0x418a04fa), SkBits2Float(0xc2a25fd2));
 path.lineTo(SkBits2Float(0x41478bd6), SkBits2Float(0xc26ac20e));
@@ -758,7 +758,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x418a04fd), SkBits2Float(0xc2a25fd2));
 path.cubicTo(SkBits2Float(0x418e8d81), SkBits2Float(0xc2a2222a), SkBits2Float(0x41931368), SkBits2Float(0xc2a1e17a), SkBits2Float(0x41979681), SkBits2Float(0xc2a19dc3));
 path.lineTo(SkBits2Float(0x415b29c8), SkBits2Float(0xc269a97e));
@@ -773,7 +773,7 @@
 
 static void battleOp27(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40b98c15), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41394aaf), SkBits2Float(0xc2a4c8e8), SkBits2Float(0x418a04fd), SkBits2Float(0xc2a25fd2));
@@ -785,7 +785,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41979680), SkBits2Float(0xc2a19dc4));
 path.cubicTo(SkBits2Float(0x41e0e1b2), SkBits2Float(0xc29d51d4), SkBits2Float(0x42135c08), SkBits2Float(0xc295f036), SkBits2Float(0x42330e86), SkBits2Float(0xc28bc9b7));
 path.lineTo(SkBits2Float(0x42017048), SkBits2Float(0xc24a1a63));
@@ -800,7 +800,7 @@
 
 static void battleOp28(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40dd1e63), SkBits2Float(0xc2a5ffff), SkBits2Float(0x415caf98), SkBits2Float(0xc2a44632), SkBits2Float(0x41a3e96c), SkBits2Float(0xc2a0dcda));
 path.lineTo(SkBits2Float(0x416cfb1c), SkBits2Float(0xc2689294));
@@ -810,7 +810,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41a3e96b), SkBits2Float(0xc2a0dcda));
 path.cubicTo(SkBits2Float(0x41a94306), SkBits2Float(0xc2a085a1), SkBits2Float(0x41ae9839), SkBits2Float(0xc2a02a23), SkBits2Float(0x41b3e8b2), SkBits2Float(0xc29fca67));
 path.lineTo(SkBits2Float(0x41820dff), SkBits2Float(0xc26705ca));
@@ -825,7 +825,7 @@
 
 static void battleOp29(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40dd1e62), SkBits2Float(0xc2a60000), SkBits2Float(0x415caf97), SkBits2Float(0xc2a44632), SkBits2Float(0x41a3e96b), SkBits2Float(0xc2a0dcda));
@@ -839,7 +839,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41b3e8b1), SkBits2Float(0xc29fca67));
 path.cubicTo(SkBits2Float(0x4205291f), SkBits2Float(0xc299b5bb), SkBits2Float(0x422d73c0), SkBits2Float(0xc28f4fcf), SkBits2Float(0x425064bf), SkBits2Float(0xc2813989));
 path.lineTo(SkBits2Float(0x4216a55b), SkBits2Float(0xc23ad4b9));
@@ -854,7 +854,7 @@
 
 static void battleOp30(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41028186), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4182264a), SkBits2Float(0xc2a39869), SkBits2Float(0x41c098e8), SkBits2Float(0xc29edd15));
 path.lineTo(SkBits2Float(0x418b3a1a), SkBits2Float(0xc265aeac));
@@ -864,7 +864,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41c098e9), SkBits2Float(0xc29edd15));
 path.cubicTo(SkBits2Float(0x41c6d4b6), SkBits2Float(0xc29e642a), SkBits2Float(0x41cd0950), SkBits2Float(0xc29de562), SkBits2Float(0x41d33633), SkBits2Float(0xc29d60c8));
 path.lineTo(SkBits2Float(0x4198aee4), SkBits2Float(0xc26388d7));
@@ -879,7 +879,7 @@
 
 static void battleOp31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41028186), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4182264a), SkBits2Float(0xc2a39869), SkBits2Float(0x41c098e9), SkBits2Float(0xc29edd15));
@@ -891,7 +891,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41d33633), SkBits2Float(0xc29d60c8));
 path.cubicTo(SkBits2Float(0x421be102), SkBits2Float(0xc294f1be), SkBits2Float(0x4249615f), SkBits2Float(0xc2869cbc), SkBits2Float(0x426e4d45), SkBits2Float(0xc26729aa));
 path.lineTo(SkBits2Float(0x422c4432), SkBits2Float(0xc2271b0a));
@@ -906,7 +906,7 @@
 
 static void battleOp32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4118c001), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41982d6e), SkBits2Float(0xc2a2b4b2), SkBits2Float(0x41e01284), SkBits2Float(0xc29c4333));
 path.lineTo(SkBits2Float(0x41a1fae3), SkBits2Float(0xc261ebf5));
@@ -916,7 +916,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41e01286), SkBits2Float(0xc29c4334));
 path.cubicTo(SkBits2Float(0x41e73e86), SkBits2Float(0xc29b9ea8), SkBits2Float(0x41ee5f11), SkBits2Float(0xc29af239), SkBits2Float(0x41f57356), SkBits2Float(0xc29a3dfa));
 path.lineTo(SkBits2Float(0x41b16f25), SkBits2Float(0xc25f0029));
@@ -931,7 +931,7 @@
 
 static void battleOp33(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4118c001), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41982d6e), SkBits2Float(0xc2a2b4b2), SkBits2Float(0x41e01286), SkBits2Float(0xc29c4334));
@@ -944,7 +944,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41f57359), SkBits2Float(0xc29a3dfa));
 path.cubicTo(SkBits2Float(0x42347528), SkBits2Float(0xc28ec218), SkBits2Float(0x42669614), SkBits2Float(0xc276cf04), SkBits2Float(0x4285b481), SkBits2Float(0xc244c364));
 path.lineTo(SkBits2Float(0x42414f00), SkBits2Float(0xc20e3d0e));
@@ -959,7 +959,7 @@
 
 static void battleOp34(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41360dec), SkBits2Float(0xc2a60000), SkBits2Float(0x41b5150e), SkBits2Float(0xc2a1522b), SkBits2Float(0x42044925), SkBits2Float(0xc29840e5));
 path.lineTo(SkBits2Float(0x41bf41a8), SkBits2Float(0xc25c2022));
@@ -969,7 +969,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42044925), SkBits2Float(0xc29840e4));
 path.cubicTo(SkBits2Float(0x4208721a), SkBits2Float(0xc2975992), SkBits2Float(0x420c9178), SkBits2Float(0xc296675c), SkBits2Float(0x4210a695), SkBits2Float(0xc2956a6a));
 path.lineTo(SkBits2Float(0x41d1222e), SkBits2Float(0xc25805ce));
@@ -984,7 +984,7 @@
 
 static void battleOp35(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3673fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41360dec), SkBits2Float(0xc2a60000), SkBits2Float(0x41b5150e), SkBits2Float(0xc2a1522b), SkBits2Float(0x42044925), SkBits2Float(0xc29840e5));
@@ -997,7 +997,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4210a693), SkBits2Float(0xc2956a6a));
 path.cubicTo(SkBits2Float(0x42536b4d), SkBits2Float(0xc2854182), SkBits2Float(0x4284b863), SkBits2Float(0xc254c33a), SkBits2Float(0x42950c68), SkBits2Float(0xc2122882));
 path.lineTo(SkBits2Float(0x42577de3), SkBits2Float(0xc1d35027));
@@ -1012,7 +1012,7 @@
 
 static void battleOp36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x414e6589), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ccf9e5), SkBits2Float(0xc29ffc89), SkBits2Float(0x4214a0bb), SkBits2Float(0xc2946fc8));
 path.lineTo(SkBits2Float(0x41d6e236), SkBits2Float(0xc2569b72));
@@ -1022,7 +1022,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4214a0bb), SkBits2Float(0xc2946fc9));
 path.cubicTo(SkBits2Float(0x421938a6), SkBits2Float(0xc293496b), SkBits2Float(0x421dc2c1), SkBits2Float(0xc2921574), SkBits2Float(0x42223e19), SkBits2Float(0xc290d421));
 path.lineTo(SkBits2Float(0x41ea914d), SkBits2Float(0xc251640c));
@@ -1037,7 +1037,7 @@
 
 static void battleOp37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x414e6589), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ccf9e5), SkBits2Float(0xc29ffc89), SkBits2Float(0x4214a0bb), SkBits2Float(0xc2946fc9));
@@ -1050,7 +1050,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42223e19), SkBits2Float(0xc290d422));
 path.cubicTo(SkBits2Float(0x426bbc38), SkBits2Float(0xc2787e1d), SkBits2Float(0x42916a94), SkBits2Float(0xc234ee59), SkBits2Float(0x429e2fac), SkBits2Float(0xc1c951fc));
 path.lineTo(SkBits2Float(0x4264b3f7), SkBits2Float(0xc191885f));
@@ -1065,7 +1065,7 @@
 
 static void battleOp38(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x416c96cf), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ea70fe), SkBits2Float(0xc29e1973), SkBits2Float(0x422836c6), SkBits2Float(0xc28f1d8a));
 path.lineTo(SkBits2Float(0x41f3336d), SkBits2Float(0xc24ee9f1));
@@ -1075,7 +1075,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x422836c5), SkBits2Float(0xc28f1d8b));
 path.cubicTo(SkBits2Float(0x422d4896), SkBits2Float(0xc28da02f), SkBits2Float(0x423245ea), SkBits2Float(0xc28c11a8), SkBits2Float(0x42372d65), SkBits2Float(0xc28a7261));
 path.lineTo(SkBits2Float(0x42046ad7), SkBits2Float(0xc24829ff));
@@ -1090,7 +1090,7 @@
 
 static void battleOp39(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x416c96cf), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ea70fe), SkBits2Float(0xc29e1973), SkBits2Float(0x422836c5), SkBits2Float(0xc28f1d8b));
@@ -1102,7 +1102,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42372d65), SkBits2Float(0xc28a7262));
 path.cubicTo(SkBits2Float(0x4283f2b3), SkBits2Float(0xc25f7e9c), SkBits2Float(0x429ea5c2), SkBits2Float(0xc2098801), SkBits2Float(0x42a4b292), SkBits2Float(0xc12607b1));
 path.lineTo(SkBits2Float(0x426e1def), SkBits2Float(0xc0f00b21));
@@ -1117,7 +1117,7 @@
 
 static void battleOp40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4184d4a8), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42034ddf), SkBits2Float(0xc29c0a4c), SkBits2Float(0x423a47b2), SkBits2Float(0xc289686d));
 path.lineTo(SkBits2Float(0x4206a908), SkBits2Float(0xc246a97c));
@@ -1127,7 +1127,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423a47b2), SkBits2Float(0xc289686d));
 path.cubicTo(SkBits2Float(0x423fbcc3), SkBits2Float(0xc2878eef), SkBits2Float(0x4245154e), SkBits2Float(0xc285a0be), SkBits2Float(0x424a4f85), SkBits2Float(0xc2839e81));
 path.lineTo(SkBits2Float(0x42123fa7), SkBits2Float(0xc23e4af2));
@@ -1142,7 +1142,7 @@
 
 static void battleOp41(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4196c4f9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42148669), SkBits2Float(0xc2992c23), SkBits2Float(0x424f6452), SkBits2Float(0xc281a081));
 path.lineTo(SkBits2Float(0x4215ebfd), SkBits2Float(0xc23b6999));
@@ -1152,7 +1152,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424f6452), SkBits2Float(0xc281a081));
 path.cubicTo(SkBits2Float(0x42553921), SkBits2Float(0xc27e96d1), SkBits2Float(0x425ae53b), SkBits2Float(0xc279ba9d), SkBits2Float(0x42606622), SkBits2Float(0xc274ae80));
 path.lineTo(SkBits2Float(0x42223753), SkBits2Float(0xc230e0d8));
@@ -1167,7 +1167,7 @@
 
 static void battleOp42(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4196c4f9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42148669), SkBits2Float(0xc2992c23), SkBits2Float(0x424f6452), SkBits2Float(0xc281a081));
@@ -1179,7 +1179,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42606622), SkBits2Float(0xc274ae80));
 path.cubicTo(SkBits2Float(0x429deeac), SkBits2Float(0xc220cc44), SkBits2Float(0x42b0742c), SkBits2Float(0xc1039d5c), SkBits2Float(0x42a03731), SkBits2Float(0x41adc1b3));
 path.lineTo(SkBits2Float(0x4267a314), SkBits2Float(0x417b36e3));
@@ -1194,7 +1194,7 @@
 
 static void battleOp43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41aa5d9e), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42271b56), SkBits2Float(0xc295a109), SkBits2Float(0x4264d340), SkBits2Float(0xc2708c1d));
 path.lineTo(SkBits2Float(0x42256a74), SkBits2Float(0xc22de3bf));
@@ -1204,7 +1204,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4264d342), SkBits2Float(0xc2708c1d));
 path.cubicTo(SkBits2Float(0x426aec59), SkBits2Float(0xc26abf16), SkBits2Float(0x4270cc6c), SkBits2Float(0xc264b73d), SkBits2Float(0x42767031), SkBits2Float(0xc25e77e8));
 path.lineTo(SkBits2Float(0x423225ec), SkBits2Float(0xc220d20e));
@@ -1219,7 +1219,7 @@
 
 static void battleOp44(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41aa5d9e), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42271b56), SkBits2Float(0xc295a109), SkBits2Float(0x4264d340), SkBits2Float(0xc2708c1d));
@@ -1233,7 +1233,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42767032), SkBits2Float(0xc25e77e8));
 path.cubicTo(SkBits2Float(0x42aa697a), SkBits2Float(0xc1ebd370), SkBits2Float(0x42b37ad4), SkBits2Float(0x410b48c2), SkBits2Float(0x4291d766), SkBits2Float(0x421e927b));
 path.lineTo(SkBits2Float(0x4252dae4), SkBits2Float(0x41e542d2));
@@ -1248,7 +1248,7 @@
 
 static void battleOp45(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41bfbd07), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423b0ef1), SkBits2Float(0xc2914772), SkBits2Float(0x427a1b1d), SkBits2Float(0xc25a5641));
 path.lineTo(SkBits2Float(0x4234ccaa), SkBits2Float(0xc21dd57d));
@@ -1258,7 +1258,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427a1b1e), SkBits2Float(0xc25a5642));
 path.cubicTo(SkBits2Float(0x4280286a), SkBits2Float(0xc253393c), SkBits2Float(0x42831c11), SkBits2Float(0xc24bd939), SkBits2Float(0x4285e673), SkBits2Float(0xc2443b5f));
 path.lineTo(SkBits2Float(0x42419733), SkBits2Float(0xc20ddaba));
@@ -1273,7 +1273,7 @@
 
 static void battleOp46(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41bfbd07), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423b0ef1), SkBits2Float(0xc2914772), SkBits2Float(0x427a1b1e), SkBits2Float(0xc25a5642));
@@ -1286,7 +1286,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4285e672), SkBits2Float(0xc2443b5f));
 path.cubicTo(SkBits2Float(0x42b50145), SkBits2Float(0xc1875361), SkBits2Float(0x42afc74e), SkBits2Float(0x41db6d5e), SkBits2Float(0x4272e616), SkBits2Float(0x426253de));
 path.lineTo(SkBits2Float(0x422f96e8), SkBits2Float(0x42239c3e));
@@ -1301,7 +1301,7 @@
 
 static void battleOp47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d59904), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424f13ae), SkBits2Float(0xc28c4fb7), SkBits2Float(0x4286bb70), SkBits2Float(0xc241f0ca));
 path.lineTo(SkBits2Float(0x4242cb24), SkBits2Float(0xc20c32b1));
@@ -1311,7 +1311,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4286bb71), SkBits2Float(0xc241f0ca));
 path.cubicTo(SkBits2Float(0x4289cb2b), SkBits2Float(0xc2396eee), SkBits2Float(0x428ca6e5), SkBits2Float(0xc230a410), SkBits2Float(0x428f4c27), SkBits2Float(0xc22797c0));
 path.lineTo(SkBits2Float(0x424f2d54), SkBits2Float(0xc1f24d85));
@@ -1326,7 +1326,7 @@
 
 static void battleOp48(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d59904), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424f13ae), SkBits2Float(0xc28c4fb7), SkBits2Float(0x4286bb71), SkBits2Float(0xc241f0ca));
@@ -1338,7 +1338,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428f4c27), SkBits2Float(0xc22797c0));
 path.cubicTo(SkBits2Float(0x42bc6513), SkBits2Float(0xc055a915), SkBits2Float(0x42a45eb2), SkBits2Float(0x42389acf), SkBits2Float(0x4231df29), SkBits2Float(0x428c2a69));
 path.lineTo(SkBits2Float(0x420094fc), SkBits2Float(0x424aa62f));
@@ -1353,7 +1353,7 @@
 
 static void battleOp49(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41eed329), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4265a038), SkBits2Float(0xc285ef96), SkBits2Float(0x42905111), SkBits2Float(0xc2240eac));
 path.lineTo(SkBits2Float(0x4250a68d), SkBits2Float(0xc1ed30fa));
@@ -1363,7 +1363,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42905111), SkBits2Float(0xc2240ead));
 path.cubicTo(SkBits2Float(0x429332f8), SkBits2Float(0xc219ea36), SkBits2Float(0x4295cfef), SkBits2Float(0xc20f79c4), SkBits2Float(0x4298252c), SkBits2Float(0xc204c875));
 path.lineTo(SkBits2Float(0x425bf80f), SkBits2Float(0xc1bff9b9));
@@ -1378,7 +1378,7 @@
 
 static void battleOp50(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41eed328), SkBits2Float(0xc2a60000), SkBits2Float(0x4265a038), SkBits2Float(0xc285ef96), SkBits2Float(0x42905111), SkBits2Float(0xc2240ead));
@@ -1391,7 +1391,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4298252d), SkBits2Float(0xc204c875));
 path.cubicTo(SkBits2Float(0x42ab560c), SkBits2Float(0xc1334da0), SkBits2Float(0x42aa8ee6), SkBits2Float(0x415dbf57), SkBits2Float(0x4296030d), SkBits2Float(0x420e292a));
 path.cubicTo(SkBits2Float(0x42817734), SkBits2Float(0x4264e27f), SkBits2Float(0x42365290), SkBits2Float(0x4292cae0), SkBits2Float(0x41b3e39e), SkBits2Float(0x429fcac3));
@@ -1408,7 +1408,7 @@
 
 static void battleOp51(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42044d64), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427bf9ef), SkBits2Float(0xc27d72ab), SkBits2Float(0x42984d42), SkBits2Float(0xc2041029));
 path.lineTo(SkBits2Float(0x425c3202), SkBits2Float(0xc1beef44));
@@ -1418,7 +1418,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42984d42), SkBits2Float(0xc2041029));
 path.cubicTo(SkBits2Float(0x429adc06), SkBits2Float(0xc1f08771), SkBits2Float(0x429d127e), SkBits2Float(0xc1d85b80), SkBits2Float(0x429eedcc), SkBits2Float(0xc1bfbbc5));
 path.lineTo(SkBits2Float(0x4265c6d6), SkBits2Float(0xc18a9a3f));
@@ -1433,7 +1433,7 @@
 
 static void battleOp52(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42044d64), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427bf9ef), SkBits2Float(0xc27d72ab), SkBits2Float(0x42984d42), SkBits2Float(0xc2041029));
@@ -1445,7 +1445,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429eedcc), SkBits2Float(0xc1bfbbc6));
 path.cubicTo(SkBits2Float(0x42ae408c), SkBits2Float(0x3fb7daeb), SkBits2Float(0x42a45c89), SkBits2Float(0x41e7c57e), SkBits2Float(0x42845101), SkBits2Float(0x42487bac));
 path.cubicTo(SkBits2Float(0x42488af1), SkBits2Float(0x428e8a4c), SkBits2Float(0x41c7bd0e), SkBits2Float(0x42a6f806), SkBits2Float(0xbfc7d871), SkBits2Float(0x42a5f87b));
@@ -1462,7 +1462,7 @@
 
 static void battleOp53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421216db), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4289817d), SkBits2Float(0xc26c814f), SkBits2Float(0x429ecb3a), SkBits2Float(0xc1c183ed));
 path.lineTo(SkBits2Float(0x426594dc), SkBits2Float(0xc18be3fc));
@@ -1472,7 +1472,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429ecb3a), SkBits2Float(0xc1c183e9));
 path.cubicTo(SkBits2Float(0x42a0d9cb), SkBits2Float(0xc1a68281), SkBits2Float(0x42a27999), SkBits2Float(0xc18b01ce), SkBits2Float(0x42a3a81d), SkBits2Float(0xc15e595d));
 path.lineTo(SkBits2Float(0x426c9cb2), SkBits2Float(0xc120bbfa));
@@ -1487,7 +1487,7 @@
 
 static void battleOp54(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421216db), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4289817d), SkBits2Float(0xc26c814f), SkBits2Float(0x429ecb3a), SkBits2Float(0xc1c183ed));
@@ -1500,7 +1500,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a3a81d), SkBits2Float(0xc15e595e));
 path.cubicTo(SkBits2Float(0x42ad725e), SkBits2Float(0x416ed313), SkBits2Float(0x42982fa2), SkBits2Float(0x4230cc44), SkBits2Float(0x42575fca), SkBits2Float(0x427ca963));
 path.cubicTo(SkBits2Float(0x41fcc0a1), SkBits2Float(0x42a44341), SkBits2Float(0x3f80ed4e), SkBits2Float(0x42affc4e), SkBits2Float(0xc1d56b7f), SkBits2Float(0x429d3115));
@@ -1517,7 +1517,7 @@
 
 static void battleOp55(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4220aa02), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42952310), SkBits2Float(0xc258f48d), SkBits2Float(0x42a35f68), SkBits2Float(0xc16b5614));
 path.lineTo(SkBits2Float(0x426c3395), SkBits2Float(0xc12a1f61));
@@ -1527,7 +1527,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a35f69), SkBits2Float(0xc16b5613));
 path.cubicTo(SkBits2Float(0x42a4bd24), SkBits2Float(0xc12ea3c2), SkBits2Float(0x42a59325), SkBits2Float(0xc0e282d6), SkBits2Float(0x42a5dfdf), SkBits2Float(0xc04e84a0));
 path.lineTo(SkBits2Float(0x426fd18d), SkBits2Float(0xc0154a48));
@@ -1542,7 +1542,7 @@
 
 static void battleOp56(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4220aa02), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42952310), SkBits2Float(0xc258f48d), SkBits2Float(0x42a35f69), SkBits2Float(0xc16b5613));
@@ -1555,7 +1555,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5dfdf), SkBits2Float(0xc04e84a0));
 path.cubicTo(SkBits2Float(0x42a85e4f), SkBits2Float(0x41e6959e), SkBits2Float(0x4285b4e3), SkBits2Float(0x426ae44f), SkBits2Float(0x4219b105), SkBits2Float(0x42932450));
 path.cubicTo(SkBits2Float(0x411fe111), SkBits2Float(0x42b0d679), SkBits2Float(0xc1c3966b), SkBits2Float(0x42ab1d42), SkBits2Float(0xc2482755), SkBits2Float(0x428470e8));
@@ -1572,7 +1572,7 @@
 
 static void battleOp57(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422b8e0b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x429d6dbc), SkBits2Float(0xc2494bad), SkBits2Float(0x42a54cb6), SkBits2Float(0xc0f3b760));
 path.lineTo(SkBits2Float(0x426efcca), SkBits2Float(0xc0b02e2c));
@@ -1582,7 +1582,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a54cb7), SkBits2Float(0xc0f3b757));
 path.cubicTo(SkBits2Float(0x42a60d08), SkBits2Float(0xc0628d9e), SkBits2Float(0x42a632b1), SkBits2Float(0x3f0efcd8), SkBits2Float(0x42a5bd61), SkBits2Float(0x4094a90a));
 path.lineTo(SkBits2Float(0x426f9faf), SkBits2Float(0x4056ee3d));
@@ -1597,7 +1597,7 @@
 
 static void battleOp58(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422b8e0b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x429d6dbc), SkBits2Float(0xc2494bad), SkBits2Float(0x42a54cb7), SkBits2Float(0xc0f3b757));
@@ -1609,7 +1609,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5bd62), SkBits2Float(0x4094a90c));
 path.cubicTo(SkBits2Float(0x42a1e9d4), SkBits2Float(0x421b17cd), SkBits2Float(0x426944f3), SkBits2Float(0x428879ea), SkBits2Float(0x41ceac14), SkBits2Float(0x429dc116));
 path.cubicTo(SkBits2Float(0xc0d4c6f5), SkBits2Float(0x42b30843), SkBits2Float(0xc2295516), SkBits2Float(0x429e4e8b), SkBits2Float(0xc2802142), SkBits2Float(0x4253148e));
@@ -1626,7 +1626,7 @@
 
 static void battleOp59(skiatest::Reporter* reporter, const char* filename) {  // hung
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x423693bc), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42a57249), SkBits2Float(0xc2389374), SkBits2Float(0x42a5ff3a), SkBits2Float(0xbf002494));
 path.lineTo(SkBits2Float(0x426ffee2), SkBits2Float(0xbeb944c3));
@@ -1636,7 +1636,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5ff3a), SkBits2Float(0xbf0024e6));
 path.cubicTo(SkBits2Float(0x42a60c9b), SkBits2Float(0x40752b0d), SkBits2Float(0x42a56c5d), SkBits2Float(0x410284fd), SkBits2Float(0x42a41ffb), SkBits2Float(0x414709fb));
 path.lineTo(SkBits2Float(0x426d49ff), SkBits2Float(0x410fe233));
@@ -1650,7 +1650,7 @@
 
 static void battleOp60(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e9334c2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f13342a), SkBits2Float(0xc2a5ff3c), SkBits2Float(0x3f5ccd0d), SkBits2Float(0xc2a5fdb4));
 path.lineTo(SkBits2Float(0x3f1f9d85), SkBits2Float(0xc26ffcaf));
@@ -1660,7 +1660,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f5ccd1a), SkBits2Float(0xc2a5fdb5));
 path.cubicTo(SkBits2Float(0x3f642956), SkBits2Float(0xc2a5fd8c), SkBits2Float(0x3f6b855d), SkBits2Float(0xc2a5fd63), SkBits2Float(0x3f72e163), SkBits2Float(0xc2a5fd38));
 path.lineTo(SkBits2Float(0x3f2f9381), SkBits2Float(0xc26ffbfc));
@@ -1675,7 +1675,7 @@
 
 static void battleOp61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b23f68), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e9334c2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f13342a), SkBits2Float(0xc2a5ff3c), SkBits2Float(0x3f5ccd1a), SkBits2Float(0xc2a5fdb5));
@@ -1687,7 +1687,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f72e162), SkBits2Float(0xc2a5fd39));
 path.cubicTo(SkBits2Float(0x3fb51288), SkBits2Float(0xc2a5fa80), SkBits2Float(0x3ff0b297), SkBits2Float(0xc2a5f5c4), SkBits2Float(0x401627a5), SkBits2Float(0xc2a5ef06));
 path.lineTo(SkBits2Float(0x3fd9177b), SkBits2Float(0xc26fe773));
@@ -1702,7 +1702,7 @@
 
 static void battleOp62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f614848), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fe14683), SkBits2Float(0xc2a5f8d5), SkBits2Float(0x4028ee0f), SkBits2Float(0xc2a5ea81));
 path.lineTo(SkBits2Float(0x3ff43c76), SkBits2Float(0xc26fe0ec));
@@ -1712,7 +1712,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4028ee15), SkBits2Float(0xc2a5ea81));
 path.cubicTo(SkBits2Float(0x402e8f25), SkBits2Float(0xc2a5e912), SkBits2Float(0x40343026), SkBits2Float(0xc2a5e791), SkBits2Float(0x4039d111), SkBits2Float(0xc2a5e5fd));
 path.lineTo(SkBits2Float(0x4006533c), SkBits2Float(0xc26fda66));
@@ -1727,7 +1727,7 @@
 
 static void battleOp63(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f614848), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fe14683), SkBits2Float(0xc2a5f8d5), SkBits2Float(0x4028ee15), SkBits2Float(0xc2a5ea81));
@@ -1744,7 +1744,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4039d102), SkBits2Float(0xc2a5e5fe));
 path.cubicTo(SkBits2Float(0x408a83ff), SkBits2Float(0xc2a5cc72), SkBits2Float(0x40b8130f), SkBits2Float(0xc2a5a01a), SkBits2Float(0x40e58a06), SkBits2Float(0xc2a56100));
 path.lineTo(SkBits2Float(0x40a5ee90), SkBits2Float(0xc26f1a20));
@@ -1759,7 +1759,7 @@
 
 static void battleOp64(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3faf587e), SkBits2Float(0xc2a5ffff), SkBits2Float(0x402f5505), SkBits2Float(0xc2a5eea1), SkBits2Float(0x408372de), SkBits2Float(0xc2a5cbeb));
 path.lineTo(SkBits2Float(0x403e0bd0), SkBits2Float(0xc26fb4b6));
@@ -1769,7 +1769,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x408372d6), SkBits2Float(0xc2a5cbec));
 path.cubicTo(SkBits2Float(0x4087d39d), SkBits2Float(0xc2a5c874), SkBits2Float(0x408c3440), SkBits2Float(0xc2a5c4cf), SkBits2Float(0x409094bd), SkBits2Float(0xc2a5c0fe));
 path.lineTo(SkBits2Float(0x40510866), SkBits2Float(0xc26fa4e7));
@@ -1784,7 +1784,7 @@
 
 static void battleOp65(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x363f7eb2), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3faf5872), SkBits2Float(0xc2a60000), SkBits2Float(0x402f54f9), SkBits2Float(0xc2a5eea1), SkBits2Float(0x408372d5), SkBits2Float(0xc2a5cbeb));
@@ -1797,7 +1797,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x409094be), SkBits2Float(0xc2a5c0fe));
 path.cubicTo(SkBits2Float(0x40d784bb), SkBits2Float(0xc2a5831d), SkBits2Float(0x410f22d3), SkBits2Float(0xc2a517ba), SkBits2Float(0x413255ec), SkBits2Float(0xc2a47f15));
 path.lineTo(SkBits2Float(0x4100ead4), SkBits2Float(0xc26dd37e));
@@ -1812,7 +1812,7 @@
 
 static void battleOp66(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4037e518), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40b7d534), SkBits2Float(0xc2a5b39a), SkBits2Float(0x4109a47d), SkBits2Float(0xc2a51b1f));
 path.lineTo(SkBits2Float(0x40c70051), SkBits2Float(0xc26eb519));
@@ -1822,7 +1822,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4109a47c), SkBits2Float(0xc2a51b20));
 path.cubicTo(SkBits2Float(0x410e36d1), SkBits2Float(0xc2a50be2), SkBits2Float(0x4112c883), SkBits2Float(0xc2a4fbe1), SkBits2Float(0x41175985), SkBits2Float(0xc2a4eb1d));
 path.lineTo(SkBits2Float(0x40dad196), SkBits2Float(0xc26e6faf));
@@ -1837,7 +1837,7 @@
 
 static void battleOp67(skiatest::Reporter* reporter, const char* filename) { // crashed
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4037e518), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40b7d534), SkBits2Float(0xc2a5b39a), SkBits2Float(0x4109a47c), SkBits2Float(0xc2a51b20));
@@ -1850,7 +1850,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4117597f), SkBits2Float(0xc2a4eb1d));
 path.cubicTo(SkBits2Float(0x41616445), SkBits2Float(0xc2a3db51), SkBits2Float(0x41954b2d), SkBits2Float(0xc2a2048b), SkBits2Float(0x41b914a4), SkBits2Float(0xc29f6bcb));
 path.lineTo(SkBits2Float(0x4185cb10), SkBits2Float(0xc2667d00));
@@ -1864,7 +1864,7 @@
 
 static void battleOp68(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e1b2207), SkBits2Float(0xc2a60000), SkBits2Float(0x3e9b2105), SkBits2Float(0xc2a5ffca), SkBits2Float(0x3ee8b0c0), SkBits2Float(0xc2a5ff5d));
 path.lineTo(SkBits2Float(0x3ea83563), SkBits2Float(0xc26fff14));
@@ -1874,7 +1874,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee8b040), SkBits2Float(0xc2a5ff5d));
 path.cubicTo(SkBits2Float(0x3ef0720a), SkBits2Float(0xc2a5ff52), SkBits2Float(0x3ef83386), SkBits2Float(0xc2a5ff47), SkBits2Float(0x3efff501), SkBits2Float(0xc2a5ff3b));
 path.lineTo(SkBits2Float(0x3eb90778), SkBits2Float(0xc26ffee3));
@@ -1889,7 +1889,7 @@
 
 static void battleOp69(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b67768), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e1b21b2), SkBits2Float(0xc2a60000), SkBits2Float(0x3e9b20b0), SkBits2Float(0xc2a5ffca), SkBits2Float(0x3ee8b040), SkBits2Float(0xc2a5ff5d));
@@ -1902,7 +1902,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3efff501), SkBits2Float(0xc2a5ff3b));
 path.cubicTo(SkBits2Float(0x3f3ed289), SkBits2Float(0xc2a5fe79), SkBits2Float(0x3f7daa5c), SkBits2Float(0xc2a5fd28), SkBits2Float(0x3f9e4099), SkBits2Float(0xc2a5fb49));
 path.lineTo(SkBits2Float(0x3f64cc5f), SkBits2Float(0xc26ff92f));
@@ -1917,7 +1917,7 @@
 
 static void battleOp70(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f0938d2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f893841), SkBits2Float(0xc2a5fd56), SkBits2Float(0x3fcdd137), SkBits2Float(0xc2a5f805));
 path.lineTo(SkBits2Float(0x3f94c89b), SkBits2Float(0xc26ff478));
@@ -1927,7 +1927,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fcdd13c), SkBits2Float(0xc2a5f806));
 path.cubicTo(SkBits2Float(0x3fd4ad55), SkBits2Float(0xc2a5f77d), SkBits2Float(0x3fdb895f), SkBits2Float(0xc2a5f6ef), SkBits2Float(0x3fe26560), SkBits2Float(0xc2a5f659));
 path.lineTo(SkBits2Float(0x3fa3a8ea), SkBits2Float(0xc26ff20c));
@@ -1942,7 +1942,7 @@
 
 static void battleOp71(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x360ebeb2), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f0938d2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f893841), SkBits2Float(0xc2a5fd56), SkBits2Float(0x3fcdd13c), SkBits2Float(0xc2a5f806));
@@ -1954,7 +1954,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fe26566), SkBits2Float(0xc2a5f65a));
 path.cubicTo(SkBits2Float(0x4028c729), SkBits2Float(0xc2a5ecdf), SkBits2Float(0x406055f2), SkBits2Float(0xc2a5dc6a), SkBits2Float(0x408beceb), SkBits2Float(0xc2a5c4fb));
 path.lineTo(SkBits2Float(0x404a4d47), SkBits2Float(0xc26faaae));
@@ -1969,7 +1969,7 @@
 
 static void battleOp72(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f73aa4a), SkBits2Float(0xc2a60000), SkBits2Float(0x3ff3a7f0), SkBits2Float(0xc2a5f79e), SkBits2Float(0x4036b54b), SkBits2Float(0xc2a5e6db));
 path.lineTo(SkBits2Float(0x40041412), SkBits2Float(0xc26fdba5));
@@ -1979,7 +1979,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4036b55d), SkBits2Float(0xc2a5e6db));
 path.cubicTo(SkBits2Float(0x403ccbdf), SkBits2Float(0xc2a5e52d), SkBits2Float(0x4042e24c), SkBits2Float(0xc2a5e36a), SkBits2Float(0x4048f89e), SkBits2Float(0xc2a5e192));
 path.lineTo(SkBits2Float(0x401147bc), SkBits2Float(0xc26fd403));
@@ -1994,7 +1994,7 @@
 
 static void battleOp73(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40447e19), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40c46ab2), SkBits2Float(0xc2a5a8c7), SkBits2Float(0x4113078c), SkBits2Float(0xc2a4fabe));
 path.lineTo(SkBits2Float(0x40d4929e), SkBits2Float(0xc26e8647));
@@ -2004,7 +2004,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4113078b), SkBits2Float(0xc2a4fabe));
 path.cubicTo(SkBits2Float(0x4117e908), SkBits2Float(0xc2a4e957), SkBits2Float(0x411cc9c0), SkBits2Float(0xc2a4d714), SkBits2Float(0x4121a9a1), SkBits2Float(0xc2a4c3f3));
 path.lineTo(SkBits2Float(0x40e9baad), SkBits2Float(0xc26e370e));
@@ -2019,7 +2019,7 @@
 
 static void battleOp74(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x406db78d), SkBits2Float(0xc2a60000), SkBits2Float(0x40ed953d), SkBits2Float(0xc2a58058), SkBits2Float(0x4131afb7), SkBits2Float(0xc2a481e4));
 path.lineTo(SkBits2Float(0x410072b2), SkBits2Float(0xc26dd78e));
@@ -2029,7 +2029,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4131afba), SkBits2Float(0xc2a481e4));
 path.cubicTo(SkBits2Float(0x413792dd), SkBits2Float(0xc2a46874), SkBits2Float(0x413d74a2), SkBits2Float(0xc2a44dc1), SkBits2Float(0x414354e9), SkBits2Float(0xc2a431ca));
 path.lineTo(SkBits2Float(0x410d3424), SkBits2Float(0xc26d63c0));
@@ -2044,7 +2044,7 @@
 
 static void battleOp75(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x406db78d), SkBits2Float(0xc2a60000), SkBits2Float(0x40ed953d), SkBits2Float(0xc2a58058), SkBits2Float(0x4131afba), SkBits2Float(0xc2a481e4));
@@ -2056,7 +2056,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x414354ed), SkBits2Float(0xc2a431cb));
 path.cubicTo(SkBits2Float(0x419152e5), SkBits2Float(0xc2a26c3a), SkBits2Float(0x41c0119b), SkBits2Float(0xc29f5c06), SkBits2Float(0x41ed1335), SkBits2Float(0xc29b0f0a));
 path.lineTo(SkBits2Float(0x41ab612b), SkBits2Float(0xc2602e6b));
@@ -2071,7 +2071,7 @@
 
 static void battleOp76(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40932e58), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41130dbc), SkBits2Float(0xc2a53c41), SkBits2Float(0x415ba178), SkBits2Float(0xc2a3b6ca));
 path.lineTo(SkBits2Float(0x411ec4eb), SkBits2Float(0xc26cb1eb));
@@ -2081,7 +2081,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x415ba178), SkBits2Float(0xc2a3b6cb));
 path.cubicTo(SkBits2Float(0x4162e261), SkBits2Float(0xc2a38fde), SkBits2Float(0x416a20aa), SkBits2Float(0xc2a36704), SkBits2Float(0x41715c23), SkBits2Float(0xc2a33c3e));
 path.lineTo(SkBits2Float(0x412e7a25), SkBits2Float(0xc26c00bd));
@@ -2096,7 +2096,7 @@
 
 static void battleOp77(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40d0158a), SkBits2Float(0xc2a60000), SkBits2Float(0x414fb944), SkBits2Float(0xc2a478c0), SkBits2Float(0x419a74b5), SkBits2Float(0xc2a1724b));
 path.lineTo(SkBits2Float(0x415f4f4c), SkBits2Float(0xc2696aa5));
@@ -2106,7 +2106,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x419a74b6), SkBits2Float(0xc2a1724b));
 path.cubicTo(SkBits2Float(0x419f8274), SkBits2Float(0xc2a124ef), SkBits2Float(0x41a48c82), SkBits2Float(0xc2a0d3c9), SkBits2Float(0x41a9929f), SkBits2Float(0xc2a07edb));
 path.lineTo(SkBits2Float(0x41752a58), SkBits2Float(0xc2680ab0));
@@ -2121,7 +2121,7 @@
 
 static void battleOp78(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3655fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40d0158a), SkBits2Float(0xc2a60000), SkBits2Float(0x414fb944), SkBits2Float(0xc2a478c0), SkBits2Float(0x419a74b6), SkBits2Float(0xc2a1724b));
@@ -2133,7 +2133,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41a9929f), SkBits2Float(0xc2a07edc));
 path.cubicTo(SkBits2Float(0x41fb3aee), SkBits2Float(0xc29b1a71), SkBits2Float(0x422402f4), SkBits2Float(0xc291ddaf), SkBits2Float(0x4245eaa6), SkBits2Float(0xc2854763));
 path.lineTo(SkBits2Float(0x420f1280), SkBits2Float(0xc240b13c));
@@ -2148,7 +2148,7 @@
 
 static void battleOp79(skiatest::Reporter* reporter, const char* filename) {  //crashed
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4110a0cc), SkBits2Float(0xc2a60000), SkBits2Float(0x4190247a), SkBits2Float(0xc2a30bfe), SkBits2Float(0x41d4a5dc), SkBits2Float(0xc29d41d4));
 path.lineTo(SkBits2Float(0x4199b8a9), SkBits2Float(0xc2635c16));
@@ -2158,7 +2158,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41d4a5d9), SkBits2Float(0xc29d41d4));
 path.cubicTo(SkBits2Float(0x41db7bbd), SkBits2Float(0xc29cadef), SkBits2Float(0x41e247df), SkBits2Float(0xc29c12ec), SkBits2Float(0x41e9098d), SkBits2Float(0xc29b70d9));
 path.lineTo(SkBits2Float(0x41a875f1), SkBits2Float(0xc260bbd5));
@@ -2172,7 +2172,7 @@
 
 static void battleOp80(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e15a675), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e95a67a), SkBits2Float(0xc2a5ffcd), SkBits2Float(0x3ee07980), SkBits2Float(0xc2a5ff68));
 path.lineTo(SkBits2Float(0x3ea245bb), SkBits2Float(0xc26fff25));
@@ -2182,7 +2182,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee07a10), SkBits2Float(0xc2a5ff68));
 path.cubicTo(SkBits2Float(0x3ee7f565), SkBits2Float(0xc2a5ff5d), SkBits2Float(0x3eef70d9), SkBits2Float(0xc2a5ff52), SkBits2Float(0x3ef6ec4d), SkBits2Float(0xc2a5ff47));
 path.lineTo(SkBits2Float(0x3eb27fdb), SkBits2Float(0xc26ffef6));
@@ -2197,7 +2197,7 @@
 
 static void battleOp81(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3691e768), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e15a675), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e95a67a), SkBits2Float(0xc2a5ffcd), SkBits2Float(0x3ee07a10), SkBits2Float(0xc2a5ff68));
@@ -2209,7 +2209,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ef6ec9b), SkBits2Float(0xc2a5ff48));
 path.cubicTo(SkBits2Float(0x3f3816c9), SkBits2Float(0xc2a5fe94), SkBits2Float(0x3f74b6e1), SkBits2Float(0xc2a5fd5b), SkBits2Float(0x3f98ab0b), SkBits2Float(0xc2a5fb9d));
 path.lineTo(SkBits2Float(0x3f5cb973), SkBits2Float(0xc26ff9a8));
@@ -2224,7 +2224,7 @@
 
 static void battleOp82(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3eff98a5), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f7f97b3), SkBits2Float(0xc2a5fdb1), SkBits2Float(0x3fbfaf38), SkBits2Float(0xc2a5f914));
 path.lineTo(SkBits2Float(0x3f8a9112), SkBits2Float(0xc26ff600));
@@ -2234,7 +2234,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fbfaf15), SkBits2Float(0xc2a5f915));
 path.cubicTo(SkBits2Float(0x3fc612b4), SkBits2Float(0xc2a5f8a0), SkBits2Float(0x3fcc7634), SkBits2Float(0xc2a5f824), SkBits2Float(0x3fd2d9ad), SkBits2Float(0xc2a5f7a2));
 path.lineTo(SkBits2Float(0x3f986bef), SkBits2Float(0xc26ff3e6));
@@ -2249,7 +2249,7 @@
 
 static void battleOp83(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3eff9875), SkBits2Float(0xc2a60000), SkBits2Float(0x3f7f9783), SkBits2Float(0xc2a5fdb1), SkBits2Float(0x3fbfaf14), SkBits2Float(0xc2a5f914));
@@ -2262,7 +2262,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fd2d994), SkBits2Float(0xc2a5f7a1));
 path.cubicTo(SkBits2Float(0x401d305c), SkBits2Float(0xc2a5ef69), SkBits2Float(0x4050ef71), SkBits2Float(0xc2a5e123), SkBits2Float(0x408252dc), SkBits2Float(0xc2a5ccd0));
 path.lineTo(SkBits2Float(0x403c6b7d), SkBits2Float(0xc26fb5fe));
@@ -2277,7 +2277,7 @@
 
 static void battleOp84(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f541e8b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fd41d19), SkBits2Float(0xc2a5f9a6), SkBits2Float(0x401f1022), SkBits2Float(0xc2a5ecf2));
 path.lineTo(SkBits2Float(0x3fe5f882), SkBits2Float(0xc26fe473));
@@ -2287,7 +2287,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x401f1027), SkBits2Float(0xc2a5ecf2));
 path.cubicTo(SkBits2Float(0x40245d21), SkBits2Float(0xc2a5ebac), SkBits2Float(0x4029aa04), SkBits2Float(0xc2a5ea57), SkBits2Float(0x402ef6d6), SkBits2Float(0xc2a5e8f1));
 path.lineTo(SkBits2Float(0x3ffcf5ba), SkBits2Float(0xc26fdeaa));
@@ -2302,7 +2302,7 @@
 
 static void battleOp85(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f541e8b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fd41d19), SkBits2Float(0xc2a5f9a6), SkBits2Float(0x401f1027), SkBits2Float(0xc2a5ecf2));
@@ -2314,7 +2314,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x402ef6c3), SkBits2Float(0xc2a5e8f1));
 path.cubicTo(SkBits2Float(0x40826d68), SkBits2Float(0xc2a5d24c), SkBits2Float(0x40ad550a), SkBits2Float(0xc2a5aafb), SkBits2Float(0x40d82890), SkBits2Float(0xc2a57308));
 path.lineTo(SkBits2Float(0x409c425c), SkBits2Float(0xc26f3430));
@@ -2329,7 +2329,7 @@
 
 static void battleOp86(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40155bee), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40955364), SkBits2Float(0xc2a5cd99), SkBits2Float(0x40dfbd5f), SkBits2Float(0xc2a568f2));
 path.lineTo(SkBits2Float(0x40a1bd53), SkBits2Float(0xc26f259d));
@@ -2339,7 +2339,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40dfbd5e), SkBits2Float(0xc2a568f3));
 path.cubicTo(SkBits2Float(0x40e72e1b), SkBits2Float(0xc2a55ee2), SkBits2Float(0x40ee9e1c), SkBits2Float(0xc2a55452), SkBits2Float(0x40f60d62), SkBits2Float(0xc2a54941));
 path.lineTo(SkBits2Float(0x40b1de84), SkBits2Float(0xc26ef7c9));
@@ -2354,7 +2354,7 @@
 
 static void battleOp87(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3619fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40155bee), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40955364), SkBits2Float(0xc2a5cd99), SkBits2Float(0x40dfbd5e), SkBits2Float(0xc2a568f3));
@@ -2367,7 +2367,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40f60d69), SkBits2Float(0xc2a54941));
 path.cubicTo(SkBits2Float(0x41374a21), SkBits2Float(0xc2a495d5), SkBits2Float(0x41731962), SkBits2Float(0xc2a35eca), SkBits2Float(0x419704b1), SkBits2Float(0xc2a1a64c));
 path.lineTo(SkBits2Float(0x415a56f5), SkBits2Float(0xc269b5d4));
@@ -2382,7 +2382,7 @@
 
 static void battleOp88(skiatest::Reporter* reporter, const char* filename) {  // crashed
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4059d383), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40d9b918), SkBits2Float(0xc2a594d0), SkBits2Float(0x4122e820), SkBits2Float(0xc2a4bf0c));
 path.lineTo(SkBits2Float(0x40eb871c), SkBits2Float(0xc26e2ff8));
@@ -2392,7 +2392,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4122e81e), SkBits2Float(0xc2a4bf0c));
 path.cubicTo(SkBits2Float(0x41284f3c), SkBits2Float(0xc2a4a9ac), SkBits2Float(0x412db549), SkBits2Float(0xc2a4933e), SkBits2Float(0x41331a33), SkBits2Float(0xc2a47bbf));
 path.lineTo(SkBits2Float(0x410178be), SkBits2Float(0xc26dceac));
@@ -2406,7 +2406,7 @@
 
 static void battleOp89(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3dd41fb8), SkBits2Float(0xc2a5fffe), SkBits2Float(0x3e541e5b), SkBits2Float(0xc2a5ffe5), SkBits2Float(0x3e9f1657), SkBits2Float(0xc2a5ffb2));
 path.lineTo(SkBits2Float(0x3e66012b), SkBits2Float(0xc26fff92));
@@ -2416,7 +2416,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3e9f1626), SkBits2Float(0xc2a5ffb4));
 path.cubicTo(SkBits2Float(0x3ea463a8), SkBits2Float(0xc2a5ffae), SkBits2Float(0x3ea9b10b), SkBits2Float(0xc2a5ffa8), SkBits2Float(0x3eaefe6d), SkBits2Float(0xc2a5ffa3));
 path.lineTo(SkBits2Float(0x3e7d0144), SkBits2Float(0xc26fff7b));
@@ -2431,7 +2431,7 @@
 
 static void battleOp90(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3dd41f74), SkBits2Float(0xc2a5fffe), SkBits2Float(0x3e541e17), SkBits2Float(0xc2a5ffe5), SkBits2Float(0x3e9f1624), SkBits2Float(0xc2a5ffb2));
@@ -2444,7 +2444,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3eaefebc), SkBits2Float(0xc2a5ffa4));
 path.cubicTo(SkBits2Float(0x3f0276b7), SkBits2Float(0xc2a5ff4a), SkBits2Float(0x3f2d6dea), SkBits2Float(0xc2a5feac), SkBits2Float(0x3f5864cc), SkBits2Float(0xc2a5fdcd));
 path.lineTo(SkBits2Float(0x3f1c6df6), SkBits2Float(0xc26ffcd0));
@@ -2459,7 +2459,7 @@
 
 static void battleOp91(skiatest::Reporter* reporter, const char* filename) {  // crashed
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ec1e1ad), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f41e136), SkBits2Float(0xc2a5feac), SkBits2Float(0x3f9167c6), SkBits2Float(0xc2a5fc05));
 path.lineTo(SkBits2Float(0x3f523979), SkBits2Float(0xc26ffa3f));
@@ -2469,7 +2469,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f9167c1), SkBits2Float(0xc2a5fc05));
 path.cubicTo(SkBits2Float(0x3f96406f), SkBits2Float(0xc2a5fbc1), SkBits2Float(0x3f9b1917), SkBits2Float(0xc2a5fb79), SkBits2Float(0x3f9ff1bc), SkBits2Float(0xc2a5fb2f));
 path.lineTo(SkBits2Float(0x3f673ed7), SkBits2Float(0xc26ff909));
@@ -2483,7 +2483,7 @@
 
 static void battleOp92(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e2c5962), SkBits2Float(0xc2a60000), SkBits2Float(0x3eac58ef), SkBits2Float(0xc2a5ffbd), SkBits2Float(0x3f014269), SkBits2Float(0xc2a5ff37));
 path.lineTo(SkBits2Float(0x3ebae1ca), SkBits2Float(0xc26ffedd));
@@ -2493,7 +2493,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f014292), SkBits2Float(0xc2a5ff37));
 path.cubicTo(SkBits2Float(0x3f0591a2), SkBits2Float(0xc2a5ff28), SkBits2Float(0x3f09e09b), SkBits2Float(0xc2a5ff1a), SkBits2Float(0x3f0e2f92), SkBits2Float(0xc2a5ff0b));
 path.lineTo(SkBits2Float(0x3ecd91e5), SkBits2Float(0xc26ffea0));
@@ -2508,7 +2508,7 @@
 
 static void battleOp93(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36163ed0), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.quadTo(SkBits2Float(0x3e81430a), SkBits2Float(0xc2a60000), SkBits2Float(0x3f014292), SkBits2Float(0xc2a5ff37));
@@ -2520,7 +2520,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f0e2f94), SkBits2Float(0xc2a5ff0c));
 path.cubicTo(SkBits2Float(0x3f5401b9), SkBits2Float(0xc2a5fe1c), SkBits2Float(0x3f8ce9a3), SkBits2Float(0xc2a5fc7d), SkBits2Float(0x3fafd1bd), SkBits2Float(0xc2a5fa2d));
 path.lineTo(SkBits2Float(0x3f7e3238), SkBits2Float(0xc26ff796));
@@ -2535,7 +2535,7 @@
 
 static void battleOp94(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f167e4a), SkBits2Float(0xc2a60000), SkBits2Float(0x3f967d97), SkBits2Float(0xc2a5fcce), SkBits2Float(0x3fe1b83b), SkBits2Float(0xc2a5f668));
 path.lineTo(SkBits2Float(0x3fa32ba2), SkBits2Float(0xc26ff222));
@@ -2545,7 +2545,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fe1b817), SkBits2Float(0xc2a5f668));
 path.cubicTo(SkBits2Float(0x3fe93dd6), SkBits2Float(0xc2a5f5c4), SkBits2Float(0x3ff0c3a7), SkBits2Float(0xc2a5f518), SkBits2Float(0x3ff8496b), SkBits2Float(0xc2a5f464));
 path.lineTo(SkBits2Float(0x3fb37c11), SkBits2Float(0xc26fef38));
@@ -2560,14 +2560,14 @@
 
 static void battleOp95(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f167e32), SkBits2Float(0xc2a60000), SkBits2Float(0x3f967d7f), SkBits2Float(0xc2a5fcce), SkBits2Float(0x3fe1b817), SkBits2Float(0xc2a5f668));
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ff8497f), SkBits2Float(0xc2a5f465));
 path.cubicTo(SkBits2Float(0x40391895), SkBits2Float(0xc2a5e8fe), SkBits2Float(0x407604f1), SkBits2Float(0xc2a5d533), SkBits2Float(0x40997177), SkBits2Float(0xc2a5b905));
 path.lineTo(SkBits2Float(0x405dd87f), SkBits2Float(0xc26f9962));
@@ -2582,7 +2582,7 @@
 
 static void battleOp96(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fa966bb), SkBits2Float(0xc2a5ffff), SkBits2Float(0x402963a4), SkBits2Float(0xc2a5efcb), SkBits2Float(0x407dfe39), SkBits2Float(0xc2a5cf64));
 path.lineTo(SkBits2Float(0x40379c05), SkBits2Float(0xc26fb9ba));
@@ -2592,7 +2592,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x407dfe3a), SkBits2Float(0xc2a5cf65));
 path.cubicTo(SkBits2Float(0x40833a01), SkBits2Float(0xc2a5cc27), SkBits2Float(0x408774bf), SkBits2Float(0xc2a5c8c0), SkBits2Float(0x408baf5a), SkBits2Float(0xc2a5c52f));
 path.lineTo(SkBits2Float(0x4049f448), SkBits2Float(0xc26faaf9));
@@ -2607,7 +2607,7 @@
 
 static void battleOp97(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x363f7e94), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fa966bb), SkBits2Float(0xc2a5ffff), SkBits2Float(0x402963a4), SkBits2Float(0xc2a5efcb), SkBits2Float(0x407dfe3a), SkBits2Float(0xc2a5cf65));
@@ -2620,7 +2620,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x408baf5c), SkBits2Float(0xc2a5c530));
 path.cubicTo(SkBits2Float(0x40d03963), SkBits2Float(0xc2a58b6e), SkBits2Float(0x410a4c7d), SkBits2Float(0xc2a52732), SkBits2Float(0x412c535f), SkBits2Float(0xc2a498b2));
 path.lineTo(SkBits2Float(0x40f9253d), SkBits2Float(0xc26df886));
@@ -2635,7 +2635,7 @@
 
 static void battleOp98(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40155bee), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40955364), SkBits2Float(0xc2a5cd99), SkBits2Float(0x40dfbd5f), SkBits2Float(0xc2a568f2));
 path.lineTo(SkBits2Float(0x40a1bd53), SkBits2Float(0xc26f259d));
@@ -2645,7 +2645,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40dfbd5e), SkBits2Float(0xc2a568f3));
 path.cubicTo(SkBits2Float(0x40e72e1b), SkBits2Float(0xc2a55ee2), SkBits2Float(0x40ee9e1c), SkBits2Float(0xc2a55452), SkBits2Float(0x40f60d62), SkBits2Float(0xc2a54941));
 path.lineTo(SkBits2Float(0x40b1de84), SkBits2Float(0xc26ef7c9));
@@ -2660,7 +2660,7 @@
 
 static void battleOp99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3619fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40155bee), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40955364), SkBits2Float(0xc2a5cd99), SkBits2Float(0x40dfbd5e), SkBits2Float(0xc2a568f3));
@@ -2673,7 +2673,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40f60d69), SkBits2Float(0xc2a54941));
 path.cubicTo(SkBits2Float(0x41374a21), SkBits2Float(0xc2a495d5), SkBits2Float(0x41731962), SkBits2Float(0xc2a35eca), SkBits2Float(0x419704b1), SkBits2Float(0xc2a1a64c));
 path.lineTo(SkBits2Float(0x415a56f5), SkBits2Float(0xc269b5d4));
@@ -2688,7 +2688,7 @@
 
 static void battleOp100(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x403cde0b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40bcccc9), SkBits2Float(0xc2a5af6a), SkBits2Float(0x410d5936), SkBits2Float(0xc2a50e98));
 path.lineTo(SkBits2Float(0x40cc5bf6), SkBits2Float(0xc26ea2fc));
@@ -2698,7 +2698,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x410d5935), SkBits2Float(0xc2a50e99));
 path.cubicTo(SkBits2Float(0x41120ace), SkBits2Float(0xc2a4fe85), SkBits2Float(0x4116bbb5), SkBits2Float(0xc2a4eda4), SkBits2Float(0x411b6bdd), SkBits2Float(0xc2a4dbf6));
 path.lineTo(SkBits2Float(0x40e0b4a3), SkBits2Float(0xc26e59c7));
@@ -2713,7 +2713,7 @@
 
 static void battleOp101(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x406db78d), SkBits2Float(0xc2a60000), SkBits2Float(0x40ed953d), SkBits2Float(0xc2a58058), SkBits2Float(0x4131afb7), SkBits2Float(0xc2a481e4));
 path.lineTo(SkBits2Float(0x410072b2), SkBits2Float(0xc26dd78e));
@@ -2723,7 +2723,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4131afba), SkBits2Float(0xc2a481e4));
 path.cubicTo(SkBits2Float(0x413792dd), SkBits2Float(0xc2a46874), SkBits2Float(0x413d74a2), SkBits2Float(0xc2a44dc1), SkBits2Float(0x414354e9), SkBits2Float(0xc2a431ca));
 path.lineTo(SkBits2Float(0x410d3424), SkBits2Float(0xc26d63c0));
@@ -2738,7 +2738,7 @@
 
 static void battleOp102(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x406db78d), SkBits2Float(0xc2a60000), SkBits2Float(0x40ed953d), SkBits2Float(0xc2a58058), SkBits2Float(0x4131afba), SkBits2Float(0xc2a481e4));
@@ -2750,7 +2750,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x414354ed), SkBits2Float(0xc2a431cb));
 path.cubicTo(SkBits2Float(0x419152e5), SkBits2Float(0xc2a26c3a), SkBits2Float(0x41c0119b), SkBits2Float(0xc29f5c06), SkBits2Float(0x41ed1335), SkBits2Float(0xc29b0f0a));
 path.lineTo(SkBits2Float(0x41ab612b), SkBits2Float(0xc2602e6b));
@@ -2765,7 +2765,7 @@
 
 static void battleOp103(skiatest::Reporter* reporter, const char* filename) {  //crash
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x408e2d73), SkBits2Float(0xc2a5ffff), SkBits2Float(0x410e100a), SkBits2Float(0xc2a54957), SkBits2Float(0x41543cd2), SkBits2Float(0xc2a3ddc8));
 path.lineTo(SkBits2Float(0x41196cba), SkBits2Float(0xc26cea49));
@@ -2775,7 +2775,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41543cce), SkBits2Float(0xc2a3ddc8));
 path.cubicTo(SkBits2Float(0x415b4057), SkBits2Float(0xc2a3b973), SkBits2Float(0x41624181), SkBits2Float(0xc2a39350), SkBits2Float(0x41694022), SkBits2Float(0xc2a36b60));
 path.lineTo(SkBits2Float(0x41289d63), SkBits2Float(0xc26c44e1));
@@ -2789,7 +2789,7 @@
 
 static void battleOp104(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3dd41fb8), SkBits2Float(0xc2a5fffe), SkBits2Float(0x3e541e5b), SkBits2Float(0xc2a5ffe5), SkBits2Float(0x3e9f1657), SkBits2Float(0xc2a5ffb2));
 path.lineTo(SkBits2Float(0x3e66012b), SkBits2Float(0xc26fff92));
@@ -2799,7 +2799,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3e9f1626), SkBits2Float(0xc2a5ffb4));
 path.cubicTo(SkBits2Float(0x3ea463a8), SkBits2Float(0xc2a5ffae), SkBits2Float(0x3ea9b10b), SkBits2Float(0xc2a5ffa8), SkBits2Float(0x3eaefe6d), SkBits2Float(0xc2a5ffa3));
 path.lineTo(SkBits2Float(0x3e7d0144), SkBits2Float(0xc26fff7b));
@@ -2814,7 +2814,7 @@
 
 static void battleOp105(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3dd41f74), SkBits2Float(0xc2a5fffe), SkBits2Float(0x3e541e17), SkBits2Float(0xc2a5ffe5), SkBits2Float(0x3e9f1624), SkBits2Float(0xc2a5ffb2));
@@ -2827,7 +2827,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3eaefebc), SkBits2Float(0xc2a5ffa4));
 path.cubicTo(SkBits2Float(0x3f0276b7), SkBits2Float(0xc2a5ff4a), SkBits2Float(0x3f2d6dea), SkBits2Float(0xc2a5feac), SkBits2Float(0x3f5864cc), SkBits2Float(0xc2a5fdcd));
 path.lineTo(SkBits2Float(0x3f1c6df6), SkBits2Float(0xc26ffcd0));
@@ -2842,7 +2842,7 @@
 
 static void battleOp106(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ee221f0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f622166), SkBits2Float(0xc2a5fe31), SkBits2Float(0x3fa9974d), SkBits2Float(0xc2a5fa95));
 path.lineTo(SkBits2Float(0x3f753159), SkBits2Float(0xc26ff82c));
@@ -2852,7 +2852,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fa99777), SkBits2Float(0xc2a5fa96));
 path.cubicTo(SkBits2Float(0x3faf3e7a), SkBits2Float(0xc2a5fa39), SkBits2Float(0x3fb4e596), SkBits2Float(0xc2a5f9d8), SkBits2Float(0x3fba8cad), SkBits2Float(0xc2a5f972));
 path.lineTo(SkBits2Float(0x3f86dad5), SkBits2Float(0xc26ff687));
@@ -2867,7 +2867,7 @@
 
 static void battleOp107(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ee221f0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f622166), SkBits2Float(0xc2a5fe31), SkBits2Float(0x3fa99777), SkBits2Float(0xc2a5fa96));
@@ -2879,7 +2879,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fba8c96), SkBits2Float(0xc2a5f973));
 path.cubicTo(SkBits2Float(0x400b1301), SkBits2Float(0xc2a5f303), SkBits2Float(0x4038dc7e), SkBits2Float(0xc2a5e7d6), SkBits2Float(0x40669fe4), SkBits2Float(0xc2a5d7ed));
 path.lineTo(SkBits2Float(0x4026b765), SkBits2Float(0xc26fc611));
@@ -2894,7 +2894,7 @@
 
 static void battleOp108(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f587304), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fd8713e), SkBits2Float(0xc2a5f962), SkBits2Float(0x40224ed5), SkBits2Float(0xc2a5ec27));
 path.lineTo(SkBits2Float(0x3feaa996), SkBits2Float(0xc26fe350));
@@ -2904,7 +2904,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40224ee4), SkBits2Float(0xc2a5ec28));
 path.cubicTo(SkBits2Float(0x4027b77a), SkBits2Float(0xc2a5ead6), SkBits2Float(0x402d1ffd), SkBits2Float(0xc2a5e972), SkBits2Float(0x4032886f), SkBits2Float(0xc2a5e7fe));
 path.lineTo(SkBits2Float(0x40010f64), SkBits2Float(0xc26fdd4a));
@@ -2919,7 +2919,7 @@
 
 static void battleOp109(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f587304), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3fd8713e), SkBits2Float(0xc2a5f962), SkBits2Float(0x40224ee4), SkBits2Float(0xc2a5ec28));
@@ -2931,7 +2931,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4032887d), SkBits2Float(0xc2a5e7fe));
 path.cubicTo(SkBits2Float(0x4085166b), SkBits2Float(0xc2a5d069), SkBits2Float(0x40b0dd8e), SkBits2Float(0xc2a5a77a), SkBits2Float(0x40dc8f53), SkBits2Float(0xc2a56d38));
 path.lineTo(SkBits2Float(0x409f70d9), SkBits2Float(0xc26f2bca));
@@ -2946,7 +2946,7 @@
 
 static void battleOp110(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x400cf1ae), SkBits2Float(0xc2a5ffff), SkBits2Float(0x408cea87), SkBits2Float(0xc2a5d31f), SkBits2Float(0x40d32a40), SkBits2Float(0xc2a57979));
 path.lineTo(SkBits2Float(0x4098a645), SkBits2Float(0xc26f3d83));
@@ -2956,7 +2956,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40d32a46), SkBits2Float(0xc2a5797a));
 path.cubicTo(SkBits2Float(0x40da306e), SkBits2Float(0xc2a57083), SkBits2Float(0x40e135fe), SkBits2Float(0xc2a5671a), SkBits2Float(0x40e83aef), SkBits2Float(0xc2a55d3f));
 path.lineTo(SkBits2Float(0x40a7e090), SkBits2Float(0xc26f14b1));
@@ -2971,7 +2971,7 @@
 
 static void battleOp111(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff59), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x400cf1ae), SkBits2Float(0xc2a5ffff), SkBits2Float(0x408cea87), SkBits2Float(0xc2a5d31f), SkBits2Float(0x40d32a46), SkBits2Float(0xc2a5797a));
@@ -2988,7 +2988,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40e83ae9), SkBits2Float(0xc2a55d3f));
 path.cubicTo(SkBits2Float(0x412d0232), SkBits2Float(0xc2a4bd73), SkBits2Float(0x4165854a), SkBits2Float(0xc2a3a860), SkBits2Float(0x418ea651), SkBits2Float(0xc2a21fbf));
 path.lineTo(SkBits2Float(0x414e3d91), SkBits2Float(0xc26a656a));
@@ -3003,7 +3003,7 @@
 
 static void battleOp112(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4035711d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40b561d9), SkBits2Float(0xc2a5b5a1), SkBits2Float(0x4107d050), SkBits2Float(0xc2a5212f));
 path.lineTo(SkBits2Float(0x40c45b76), SkBits2Float(0xc26ebddb));
@@ -3013,7 +3013,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4107d054), SkBits2Float(0xc2a5212f));
 path.cubicTo(SkBits2Float(0x410c5332), SkBits2Float(0xc2a51258), SkBits2Float(0x4110d578), SkBits2Float(0xc2a502c3), SkBits2Float(0x41155714), SkBits2Float(0xc2a4f271));
 path.lineTo(SkBits2Float(0x40d7e9e2), SkBits2Float(0xc26e7a46));
@@ -3028,7 +3028,7 @@
 
 static void battleOp113(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4035711d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40b561d9), SkBits2Float(0xc2a5b5a1), SkBits2Float(0x4107d054), SkBits2Float(0xc2a5212f));
@@ -3040,7 +3040,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4115571a), SkBits2Float(0xc2a4f271));
 path.cubicTo(SkBits2Float(0x415e6818), SkBits2Float(0xc2a3e9d4), SkBits2Float(0x41935478), SkBits2Float(0xc2a21f7a), SkBits2Float(0x41b6ad74), SkBits2Float(0xc29f981d));
 path.lineTo(SkBits2Float(0x41840e5b), SkBits2Float(0xc266bd14));
@@ -3055,7 +3055,7 @@
 
 static void battleOp114(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x405f6414), SkBits2Float(0xc2a60000), SkBits2Float(0x40df4798), SkBits2Float(0xc2a58f44), SkBits2Float(0x41270b42), SkBits2Float(0xc2a4ae78));
 path.lineTo(SkBits2Float(0x40f1826b), SkBits2Float(0xc26e1801));
@@ -3065,7 +3065,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41270b46), SkBits2Float(0xc2a4ae78));
 path.cubicTo(SkBits2Float(0x412c952a), SkBits2Float(0xc2a497ff), SkBits2Float(0x41321de3), SkBits2Float(0xc2a48068), SkBits2Float(0x4137a563), SkBits2Float(0xc2a467b4));
 path.lineTo(SkBits2Float(0x4104c195), SkBits2Float(0xc26db1b1));
@@ -3080,7 +3080,7 @@
 
 static void battleOp115(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x405f6414), SkBits2Float(0xc2a60000), SkBits2Float(0x40df4798), SkBits2Float(0xc2a58f44), SkBits2Float(0x41270b46), SkBits2Float(0xc2a4ae78));
@@ -3092,7 +3092,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4137a563), SkBits2Float(0xc2a467b4));
 path.cubicTo(SkBits2Float(0x4188a9bf), SkBits2Float(0xc2a2d700), SkBits2Float(0x41b4bec4), SkBits2Float(0xc2a021d5), SkBits2Float(0x41df619b), SkBits2Float(0xc29c5308));
 path.lineTo(SkBits2Float(0x41a17afe), SkBits2Float(0xc26202d7));
@@ -3107,7 +3107,7 @@
 
 static void battleOp116(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40894a00), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41092f84), SkBits2Float(0xc2a555af), SkBits2Float(0x414d01d5), SkBits2Float(0xc2a40295));
 path.lineTo(SkBits2Float(0x411432a9), SkBits2Float(0xc26d1f80));
@@ -3117,7 +3117,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x414d01d1), SkBits2Float(0xc2a40296));
 path.cubicTo(SkBits2Float(0x4153c92e), SkBits2Float(0xc2a3e0b1), SkBits2Float(0x415a8e6d), SkBits2Float(0xc2a3bd1e), SkBits2Float(0x41615162), SkBits2Float(0xc2a397de));
 path.lineTo(SkBits2Float(0x4122e164), SkBits2Float(0xc26c8535));
@@ -3132,7 +3132,7 @@
 
 static void battleOp117(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x408949fd), SkBits2Float(0xc2a60000), SkBits2Float(0x41092f81), SkBits2Float(0xc2a555af), SkBits2Float(0x414d01d0), SkBits2Float(0xc2a40295));
@@ -3145,7 +3145,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41615164), SkBits2Float(0xc2a397de));
 path.cubicTo(SkBits2Float(0x41a78432), SkBits2Float(0xc2a13b6d), SkBits2Float(0x41dcf7f2), SkBits2Float(0xc29d27e8), SkBits2Float(0x4207e0f5), SkBits2Float(0xc29775db));
 path.lineTo(SkBits2Float(0x41c47380), SkBits2Float(0xc25afa96));
@@ -3160,7 +3160,7 @@
 
 static void battleOp118(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40a2e582), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4122b94f), SkBits2Float(0xc2a51039), SkBits2Float(0x4172cca0), SkBits2Float(0xc2a333b4));
 path.lineTo(SkBits2Float(0x412f847d), SkBits2Float(0xc26bf464));
@@ -3170,7 +3170,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4172cc9b), SkBits2Float(0xc2a333b4));
 path.cubicTo(SkBits2Float(0x417acd1a), SkBits2Float(0xc2a30415), SkBits2Float(0x41816508), SkBits2Float(0xc2a2d21d), SkBits2Float(0x4185619b), SkBits2Float(0xc2a29dcb));
 path.lineTo(SkBits2Float(0x4140d724), SkBits2Float(0xc26b1ba8));
@@ -3185,7 +3185,7 @@
 
 static void battleOp119(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40a2e57f), SkBits2Float(0xc2a60000), SkBits2Float(0x4122b94c), SkBits2Float(0xc2a51039), SkBits2Float(0x4172cc9b), SkBits2Float(0xc2a333b4));
@@ -3199,7 +3199,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4185619b), SkBits2Float(0xc2a29dcc));
 path.cubicTo(SkBits2Float(0x41c61a92), SkBits2Float(0xc29f4c69), SkBits2Float(0x42023dd6), SkBits2Float(0xc299958f), SkBits2Float(0x421f3a98), SkBits2Float(0xc291a994));
 path.lineTo(SkBits2Float(0x41e635e1), SkBits2Float(0xc25298a5));
@@ -3214,7 +3214,7 @@
 
 static void battleOp120(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40c39389), SkBits2Float(0xc2a60000), SkBits2Float(0x414346f4), SkBits2Float(0xc2a4a65f), SkBits2Float(0x419158cf), SkBits2Float(0xc2a1f965));
 path.lineTo(SkBits2Float(0x415223e0), SkBits2Float(0xc26a2df8));
@@ -3224,7 +3224,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x419158d0), SkBits2Float(0xc2a1f965));
 path.cubicTo(SkBits2Float(0x41961cea), SkBits2Float(0xc2a1b4f6), SkBits2Float(0x419addf6), SkBits2Float(0xc2a16d2c), SkBits2Float(0x419f9bbb), SkBits2Float(0xc2a12207));
 path.lineTo(SkBits2Float(0x4166c251), SkBits2Float(0xc268f69a));
@@ -3239,7 +3239,7 @@
 
 static void battleOp121(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40c39389), SkBits2Float(0xc2a60000), SkBits2Float(0x414346f4), SkBits2Float(0xc2a4a65f), SkBits2Float(0x419158d0), SkBits2Float(0xc2a1f965));
@@ -3251,7 +3251,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x419f9bbc), SkBits2Float(0xc2a12208));
 path.cubicTo(SkBits2Float(0x41eca53e), SkBits2Float(0xc29c5d1a), SkBits2Float(0x421ad1be), SkBits2Float(0xc2942e2b), SkBits2Float(0x423b8fe1), SkBits2Float(0xc288f8a3));
 path.lineTo(SkBits2Float(0x42079647), SkBits2Float(0xc24607dc));
@@ -3266,7 +3266,7 @@
 
 static void battleOp122(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x410a1653), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4189aa2f), SkBits2Float(0xc2a34ed0), SkBits2Float(0x41cb63be), SkBits2Float(0xc29e054b));
 path.lineTo(SkBits2Float(0x41930758), SkBits2Float(0xc26476b2));
@@ -3276,7 +3276,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41cb63c3), SkBits2Float(0xc29e054c));
 path.cubicTo(SkBits2Float(0x41d1f2f3), SkBits2Float(0xc29d7e37), SkBits2Float(0x41d879a0), SkBits2Float(0xc29cf09c), SkBits2Float(0x41def72d), SkBits2Float(0xc29c5c87));
 path.lineTo(SkBits2Float(0x41a12e10), SkBits2Float(0xc2621091));
@@ -3291,7 +3291,7 @@
 
 static void battleOp123(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x410a1653), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4189aa2f), SkBits2Float(0xc2a34ed0), SkBits2Float(0x41cb63be), SkBits2Float(0xc29e054b));
@@ -3305,7 +3305,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41def730), SkBits2Float(0xc29c5c87));
 path.cubicTo(SkBits2Float(0x422459f2), SkBits2Float(0xc292f017), SkBits2Float(0x42539427), SkBits2Float(0xc282f764), SkBits2Float(0x4278c050), SkBits2Float(0xc25be110));
 path.lineTo(SkBits2Float(0x4233d1f5), SkBits2Float(0xc21ef2e3));
@@ -3320,7 +3320,7 @@
 
 static void battleOp124(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x411fc00b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x419f1845), SkBits2Float(0xc2a265a5), SkBits2Float(0x41e9da2b), SkBits2Float(0xc29b5d43));
 path.lineTo(SkBits2Float(0x41a90cc1), SkBits2Float(0xc2609f84));
@@ -3330,7 +3330,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41e9da2e), SkBits2Float(0xc29b5d44));
 path.cubicTo(SkBits2Float(0x41f14eda), SkBits2Float(0xc29aa9b5), SkBits2Float(0x41f8b671), SkBits2Float(0xc299ed94), SkBits2Float(0x42000805), SkBits2Float(0xc29928f7));
 path.lineTo(SkBits2Float(0x41b91b05), SkBits2Float(0xc25d6faa));
@@ -3345,7 +3345,7 @@
 
 static void battleOp125(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x411fc00b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x419f1845), SkBits2Float(0xc2a265a5), SkBits2Float(0x41e9da2e), SkBits2Float(0xc29b5d44));
@@ -3357,7 +3357,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42000806), SkBits2Float(0xc29928f8));
 path.cubicTo(SkBits2Float(0x423c0231), SkBits2Float(0xc28ca034), SkBits2Float(0x426f4e95), SkBits2Float(0xc26f2095), SkBits2Float(0x4289c821), SkBits2Float(0xc2392c12));
 path.lineTo(SkBits2Float(0x424733db), SkBits2Float(0xc205dc02));
@@ -3372,7 +3372,7 @@
 
 static void battleOp126(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41379cd4), SkBits2Float(0xc2a60000), SkBits2Float(0x41b69d77), SkBits2Float(0xc2a13d93), SkBits2Float(0x42055871), SkBits2Float(0xc29805ae));
 path.lineTo(SkBits2Float(0x41c0c9e6), SkBits2Float(0xc25bca86));
@@ -3382,7 +3382,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42055872), SkBits2Float(0xc29805ae));
 path.cubicTo(SkBits2Float(0x420988d2), SkBits2Float(0xc2971a85), SkBits2Float(0x420daf5c), SkBits2Float(0xc296244f), SkBits2Float(0x4211cb64), SkBits2Float(0xc2952332));
 path.lineTo(SkBits2Float(0x41d2c988), SkBits2Float(0xc2579ed7));
@@ -3397,7 +3397,7 @@
 
 static void battleOp127(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3673fea5), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41379cd4), SkBits2Float(0xc2a60000), SkBits2Float(0x41b69d77), SkBits2Float(0xc2a13d93), SkBits2Float(0x42055872), SkBits2Float(0xc29805ae));
@@ -3409,7 +3409,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4211cb65), SkBits2Float(0xc2952332));
 path.cubicTo(SkBits2Float(0x42550406), SkBits2Float(0xc284b578), SkBits2Float(0x42859569), SkBits2Float(0xc252d13a), SkBits2Float(0x4295bbf4), SkBits2Float(0xc20f53bf));
 path.lineTo(SkBits2Float(0x42587bb2), SkBits2Float(0xc1cf3850));
@@ -3424,7 +3424,7 @@
 
 static void battleOp128(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4151cd59), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41d04f3f), SkBits2Float(0xc29fc954), SkBits2Float(0x4216e058), SkBits2Float(0xc293de54));
 path.lineTo(SkBits2Float(0x41da226b), SkBits2Float(0xc255c926));
@@ -3434,7 +3434,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4216e057), SkBits2Float(0xc293de54));
 path.cubicTo(SkBits2Float(0x421b86ea), SkBits2Float(0xc292aea0), SkBits2Float(0x42201eff), SkBits2Float(0xc29170ed), SkBits2Float(0x4224a79b), SkBits2Float(0xc290257e));
 path.lineTo(SkBits2Float(0x41ee0e15), SkBits2Float(0xc2506790));
@@ -3449,7 +3449,7 @@
 
 static void battleOp129(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4151cd58), SkBits2Float(0xc2a60000), SkBits2Float(0x41d04f3d), SkBits2Float(0xc29fc954), SkBits2Float(0x4216e057), SkBits2Float(0xc293de54));
@@ -3463,7 +3463,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4224a79b), SkBits2Float(0xc290257f));
 path.cubicTo(SkBits2Float(0x426f06c3), SkBits2Float(0xc275d105), SkBits2Float(0x42930d85), SkBits2Float(0xc2303df6), SkBits2Float(0x429f3103), SkBits2Float(0xc1bc373f));
 path.lineTo(SkBits2Float(0x42662806), SkBits2Float(0xc1880f44));
@@ -3478,7 +3478,7 @@
 
 static void battleOp130(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x417054a2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ee1405), SkBits2Float(0xc29dd904), SkBits2Float(0x422a9595), SkBits2Float(0xc28e6989));
 path.lineTo(SkBits2Float(0x41f6a0c0), SkBits2Float(0xc24de5b0));
@@ -3488,7 +3488,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x422a9596), SkBits2Float(0xc28e6989));
 path.cubicTo(SkBits2Float(0x422fb535), SkBits2Float(0xc28ce0c4), SkBits2Float(0x4234bf65), SkBits2Float(0xc28b465e), SkBits2Float(0x4239b2bc), SkBits2Float(0xc2899acc));
 path.lineTo(SkBits2Float(0x42063d5a), SkBits2Float(0xc246f24e));
@@ -3503,7 +3503,7 @@
 
 static void battleOp131(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x417054a2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41ee1405), SkBits2Float(0xc29dd904), SkBits2Float(0x422a9596), SkBits2Float(0xc28e6989));
@@ -3515,7 +3515,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4239b2bd), SkBits2Float(0xc2899acc));
 path.cubicTo(SkBits2Float(0x42859c2b), SkBits2Float(0xc25c33ca), SkBits2Float(0x42a01474), SkBits2Float(0xc203e23a), SkBits2Float(0x42a51fce), SkBits2Float(0xc1083bae));
 path.lineTo(SkBits2Float(0x426ebbdb), SkBits2Float(0xc0c4f6ab));
@@ -3530,7 +3530,7 @@
 
 static void battleOp132(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4187e175), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42063ec3), SkBits2Float(0xc29b93fb), SkBits2Float(0x423df6fd), SkBits2Float(0xc2882410));
 path.lineTo(SkBits2Float(0x420952ef), SkBits2Float(0xc244d488));
@@ -3540,7 +3540,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423df6fe), SkBits2Float(0xc2882411));
 path.cubicTo(SkBits2Float(0x42437e7a), SkBits2Float(0xc286364a), SkBits2Float(0x4248e78f), SkBits2Float(0xc2843312), SkBits2Float(0x424e304d), SkBits2Float(0xc2821b20));
 path.lineTo(SkBits2Float(0x42150d53), SkBits2Float(0xc23c1ae0));
@@ -3555,7 +3555,7 @@
 
 static void battleOp133(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4187e175), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42063ec3), SkBits2Float(0xc29b93fb), SkBits2Float(0x423df6fe), SkBits2Float(0xc2882411));
@@ -3568,7 +3568,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424e304d), SkBits2Float(0xc2821b20));
 path.cubicTo(SkBits2Float(0x4292cbf1), SkBits2Float(0xc23ef41d), SkBits2Float(0x42aa31a6), SkBits2Float(0xc1a4e14c), SkBits2Float(0x42a56158), SkBits2Float(0x40e54b3a));
 path.lineTo(SkBits2Float(0x426f1a9e), SkBits2Float(0x40a5c12f));
@@ -3583,7 +3583,7 @@
 
 static void battleOp134(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x419c5b1f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4219d929), SkBits2Float(0xc29834b3), SkBits2Float(0x4255ae76), SkBits2Float(0xc27e184c));
 path.lineTo(SkBits2Float(0x421a77f2), SkBits2Float(0xc237aede));
@@ -3593,7 +3593,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4255ae76), SkBits2Float(0xc27e184c));
 path.cubicTo(SkBits2Float(0x425b9ab5), SkBits2Float(0xc2791d33), SkBits2Float(0x426159ea), SkBits2Float(0xc273ed7b), SkBits2Float(0x4266e960), SkBits2Float(0xc26e8b92));
 path.lineTo(SkBits2Float(0x4226ec90), SkBits2Float(0xc22c713c));
@@ -3608,7 +3608,7 @@
 
 static void battleOp135(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x419c5b1f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4219d929), SkBits2Float(0xc29834b3), SkBits2Float(0x4255ae76), SkBits2Float(0xc27e184c));
@@ -3620,7 +3620,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4266e961), SkBits2Float(0xc26e8b93));
 path.cubicTo(SkBits2Float(0x42a1bfce), SkBits2Float(0xc214ebcf), SkBits2Float(0x42b1ee5a), SkBits2Float(0xc05d1412), SkBits2Float(0x429cf75a), SkBits2Float(0x41d80f2c));
 path.lineTo(SkBits2Float(0x4262f06b), SkBits2Float(0x419c2ffb));
@@ -3635,7 +3635,7 @@
 
 static void battleOp136(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ae0130), SkBits2Float(0xc2a5ffff), SkBits2Float(0x422a8737), SkBits2Float(0xc294ec91), SkBits2Float(0x42689b67), SkBits2Float(0xc26ce46c));
 path.lineTo(SkBits2Float(0x42282651), SkBits2Float(0xc22b3f58));
@@ -3645,7 +3645,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42689b68), SkBits2Float(0xc26ce46d));
 path.cubicTo(SkBits2Float(0x426ebcd2), SkBits2Float(0xc266df67), SkBits2Float(0x4274a1d2), SkBits2Float(0xc2609e09), SkBits2Float(0x427a4701), SkBits2Float(0xc25a23f2));
 path.lineTo(SkBits2Float(0x4234ec64), SkBits2Float(0xc21db11e));
@@ -3660,7 +3660,7 @@
 
 static void battleOp137(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ae0130), SkBits2Float(0xc2a5ffff), SkBits2Float(0x422a8737), SkBits2Float(0xc294ec91), SkBits2Float(0x42689b68), SkBits2Float(0xc26ce46d));
@@ -3672,7 +3672,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427a4702), SkBits2Float(0xc25a23f2));
 path.cubicTo(SkBits2Float(0x42ac7185), SkBits2Float(0xc1db2f83), SkBits2Float(0x42b35ed0), SkBits2Float(0x413e447a), SkBits2Float(0x428e4a3d), SkBits2Float(0x422afde8));
 path.lineTo(SkBits2Float(0x424db871), SkBits2Float(0x41f73799));
@@ -3687,7 +3687,7 @@
 
 static void battleOp138(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c2602d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423d7ece), SkBits2Float(0xc290b51a), SkBits2Float(0x427c92bc), SkBits2Float(0xc2577a5f));
 path.lineTo(SkBits2Float(0x42369543), SkBits2Float(0xc21bc469));
@@ -3697,7 +3697,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427c92be), SkBits2Float(0xc2577a5f));
 path.cubicTo(SkBits2Float(0x42816448), SkBits2Float(0xc25032db), SkBits2Float(0x42845689), SkBits2Float(0xc248a77c), SkBits2Float(0x42871e08), SkBits2Float(0xc240ddaa));
 path.lineTo(SkBits2Float(0x424359af), SkBits2Float(0xc20b6bce));
@@ -3712,7 +3712,7 @@
 
 static void battleOp139(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c2602d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423d7ece), SkBits2Float(0xc290b51a), SkBits2Float(0x427c92bc), SkBits2Float(0xc2577a5f));
@@ -3726,7 +3726,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42871e08), SkBits2Float(0xc240ddaa));
 path.cubicTo(SkBits2Float(0x42b615a2), SkBits2Float(0xc174ff4e), SkBits2Float(0x42aecf41), SkBits2Float(0x41edcc49), SkBits2Float(0x426bc7a7), SkBits2Float(0x4269bc09));
 path.lineTo(SkBits2Float(0x422a717e), SkBits2Float(0x4228f6f7));
@@ -3741,7 +3741,7 @@
 
 static void battleOp140(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d9e52a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4252f644), SkBits2Float(0xc28b460f), SkBits2Float(0x42887c98), SkBits2Float(0xc23cf83b));
 path.lineTo(SkBits2Float(0x42455485), SkBits2Float(0xc2089ac5));
@@ -3751,7 +3751,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42887c98), SkBits2Float(0xc23cf83b));
 path.cubicTo(SkBits2Float(0x428b8706), SkBits2Float(0xc2342f4a), SkBits2Float(0x428e5ab7), SkBits2Float(0xc22b1c84), SkBits2Float(0x4290f525), SkBits2Float(0xc221c800));
 path.lineTo(SkBits2Float(0x425193c7), SkBits2Float(0xc1e9e68d));
@@ -3766,7 +3766,7 @@
 
 static void battleOp141(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d9e52a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4252f644), SkBits2Float(0xc28b460f), SkBits2Float(0x42887c98), SkBits2Float(0xc23cf83b));
@@ -3779,7 +3779,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4290f526), SkBits2Float(0xc221c800));
 path.cubicTo(SkBits2Float(0x42bd6cdd), SkBits2Float(0xbf1a1474), SkBits2Float(0x42a13baa), SkBits2Float(0x4246de93), SkBits2Float(0x4223add7), SkBits2Float(0x42906c8a));
 path.lineTo(SkBits2Float(0x41eca4f8), SkBits2Float(0x4250ce48));
@@ -3794,7 +3794,7 @@
 
 static void battleOp142(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f6a97d), SkBits2Float(0xc2a60000), SkBits2Float(0x426c7f9e), SkBits2Float(0xc283d12f), SkBits2Float(0x4292f07c), SkBits2Float(0xc21a76e5));
 path.lineTo(SkBits2Float(0x42547147), SkBits2Float(0xc1df5274));
@@ -3804,7 +3804,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4292f07c), SkBits2Float(0xc21a76e5));
 path.cubicTo(SkBits2Float(0x4295bcf6), SkBits2Float(0xc20fd099), SkBits2Float(0x42983ed1), SkBits2Float(0xc204de6d), SkBits2Float(0x429a7333), SkBits2Float(0xc1f3598c));
 path.lineTo(SkBits2Float(0x425f4d1c), SkBits2Float(0xc1afea60));
@@ -3819,7 +3819,7 @@
 
 static void battleOp143(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f6a97d), SkBits2Float(0xc2a60000), SkBits2Float(0x426c7f9e), SkBits2Float(0xc283d12f), SkBits2Float(0x4292f07c), SkBits2Float(0xc21a76e5));
@@ -3831,7 +3831,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429a7334), SkBits2Float(0xc1f3598d));
 path.cubicTo(SkBits2Float(0x42ac9a56), SkBits2Float(0xc0ec08d5), SkBits2Float(0x42a93a4b), SkBits2Float(0x4194209c), SkBits2Float(0x42913f11), SkBits2Float(0x4220bdeb));
 path.cubicTo(SkBits2Float(0x427287b0), SkBits2Float(0x42776b87), SkBits2Float(0x421e5dc6), SkBits2Float(0x429a1372), SkBits2Float(0x4173f4a4), SkBits2Float(0x42a32ccd));
@@ -3848,7 +3848,7 @@
 
 static void battleOp144(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42079c39), SkBits2Float(0xc2a60000), SkBits2Float(0x4280cb64), SkBits2Float(0xc279860f), SkBits2Float(0x429a0d79), SkBits2Float(0xc1f758df));
 path.lineTo(SkBits2Float(0x425eba08), SkBits2Float(0xc1b2ce1f));
@@ -3858,7 +3858,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429a0d79), SkBits2Float(0xc1f758de));
 path.cubicTo(SkBits2Float(0x429c811b), SkBits2Float(0xc1deea6e), SkBits2Float(0x429e9731), SkBits2Float(0xc1c5ec3a), SkBits2Float(0x42a04ce7), SkBits2Float(0xc1ac8024));
 path.lineTo(SkBits2Float(0x4267c277), SkBits2Float(0xc17965fc));
@@ -3873,7 +3873,7 @@
 
 static void battleOp145(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42079c39), SkBits2Float(0xc2a60000), SkBits2Float(0x4280cb64), SkBits2Float(0xc279860f), SkBits2Float(0x429a0d79), SkBits2Float(0xc1f758df));
@@ -3886,7 +3886,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a04ce8), SkBits2Float(0xc1ac8024));
 path.cubicTo(SkBits2Float(0x42ae6ca1), SkBits2Float(0x4095ff41), SkBits2Float(0x42a1f1fa), SkBits2Float(0x4202ed54), SkBits2Float(0x427dc9de), SkBits2Float(0x42560b98));
 path.cubicTo(SkBits2Float(0x4237afc7), SkBits2Float(0x429494ee), SkBits2Float(0x419aa752), SkBits2Float(0x42aa57e8), SkBits2Float(0xc0f777b3), SkBits2Float(0x42a54724));
@@ -3903,7 +3903,7 @@
 
 static void battleOp146(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421472e7), SkBits2Float(0xc2a5ffff), SkBits2Float(0x428b6da4), SkBits2Float(0xc26973d7), SkBits2Float(0x429fb179), SkBits2Float(0xc1b54986));
 path.lineTo(SkBits2Float(0x4266e1be), SkBits2Float(0xc1830d0f));
@@ -3913,7 +3913,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429fb179), SkBits2Float(0xc1b54988));
 path.cubicTo(SkBits2Float(0x42a1a632), SkBits2Float(0xc199b837), SkBits2Float(0x42a3282f), SkBits2Float(0xc17b594e), SkBits2Float(0x42a43501), SkBits2Float(0xc142a7ba));
 path.lineTo(SkBits2Float(0x426d6865), SkBits2Float(0xc10cb6f0));
@@ -3928,7 +3928,7 @@
 
 static void battleOp147(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421472e7), SkBits2Float(0xc2a60000), SkBits2Float(0x428b6da4), SkBits2Float(0xc26973d8), SkBits2Float(0x429fb179), SkBits2Float(0xc1b54988));
@@ -3941,7 +3941,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a43502), SkBits2Float(0xc142a7bb));
 path.cubicTo(SkBits2Float(0x42ace9b0), SkBits2Float(0x4189ae79), SkBits2Float(0x429590d6), SkBits2Float(0x423ab1c1), SkBits2Float(0x424df762), SkBits2Float(0x428231a6));
 path.cubicTo(SkBits2Float(0x41e19a31), SkBits2Float(0x42a70a69), SkBits2Float(0xc04a3289), SkBits2Float(0x42b03133), SkBits2Float(0xc1f5f36e), SkBits2Float(0x429a3139));
@@ -3958,7 +3958,7 @@
 
 static void battleOp148(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42216831), SkBits2Float(0xc2a60000), SkBits2Float(0x4295b6bc), SkBits2Float(0xc257ea44), SkBits2Float(0x42a38b53), SkBits2Float(0xc1639572));
 path.lineTo(SkBits2Float(0x426c7311), SkBits2Float(0xc12484b9));
@@ -3968,7 +3968,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a38b52), SkBits2Float(0xc1639578));
 path.cubicTo(SkBits2Float(0x42a4def8), SkBits2Float(0xc1269090), SkBits2Float(0x42a5a99a), SkBits2Float(0xc0d1c16f), SkBits2Float(0x42a5e9be), SkBits2Float(0xc02be63c));
 path.lineTo(SkBits2Float(0x426fdfd2), SkBits2Float(0xbff8877d));
@@ -3983,7 +3983,7 @@
 
 static void battleOp149(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42216831), SkBits2Float(0xc2a60000), SkBits2Float(0x4295b6bc), SkBits2Float(0xc257ea44), SkBits2Float(0x42a38b52), SkBits2Float(0xc1639578));
@@ -3993,7 +3993,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5e9be), SkBits2Float(0xc02be63f));
 path.cubicTo(SkBits2Float(0x42a7ff8e), SkBits2Float(0x41ec1faa), SkBits2Float(0x42849fff), SkBits2Float(0x426da4e1), SkBits2Float(0x4216595b), SkBits2Float(0x429400af));
 path.cubicTo(SkBits2Float(0x410dcade), SkBits2Float(0x42b12eec), SkBits2Float(0xc1cdb135), SkBits2Float(0x42aa7b1c), SkBits2Float(0xc24c6646), SkBits2Float(0x4282cf52));
@@ -4010,7 +4010,7 @@
 
 static void battleOp150(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422dab0f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x429efeec), SkBits2Float(0xc2462810), SkBits2Float(0x42a58789), SkBits2Float(0xc0c7d837));
 path.lineTo(SkBits2Float(0x426f51d5), SkBits2Float(0xc0907750));
@@ -4020,7 +4020,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a58789), SkBits2Float(0xc0c7d840));
 path.cubicTo(SkBits2Float(0x42a626ff), SkBits2Float(0xc0078454), SkBits2Float(0x42a62824), SkBits2Float(0x4001c6d5), SkBits2Float(0x42a58af5), SkBits2Float(0x40c4fc3c));
 path.lineTo(SkBits2Float(0x426f56ca), SkBits2Float(0x408e6626));
@@ -4035,7 +4035,7 @@
 
 static void battleOp151(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422dab0f), SkBits2Float(0xc2a60000), SkBits2Float(0x429efeec), SkBits2Float(0xc2462811), SkBits2Float(0x42a58789), SkBits2Float(0xc0c7d840));
@@ -4049,7 +4049,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a58af6), SkBits2Float(0x40c4fc3d));
 path.cubicTo(SkBits2Float(0x42a06986), SkBits2Float(0x422298c3), SkBits2Float(0x42621341), SkBits2Float(0x428bdf10), SkBits2Float(0x41ba9762), SkBits2Float(0x429f4f99));
 path.cubicTo(SkBits2Float(0xc11def80), SkBits2Float(0x42b2c022), SkBits2Float(0xc236745f), SkBits2Float(0x429afb1c), SkBits2Float(0xc284c1e2), SkBits2Float(0x4247504a));
@@ -4066,7 +4066,7 @@
 
 static void battleOp152(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41b12ed4), SkBits2Float(0xc2a60000), SkBits2Float(0x422d822c), SkBits2Float(0xc2944bde), SkBits2Float(0x426bdb91), SkBits2Float(0xc269a7f3));
 path.cubicTo(SkBits2Float(0x42951a7b), SkBits2Float(0xc22ab829), SkBits2Float(0x42a66879), SkBits2Float(0xc1aaf2b1), SkBits2Float(0x42a5fe21), SkBits2Float(0x3f4744a4));
@@ -4078,7 +4078,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5fe22), SkBits2Float(0x3f4744a1));
 path.cubicTo(SkBits2Float(0x42a5e921), SkBits2Float(0x40a4df91), SkBits2Float(0x42a52322), SkBits2Float(0x411841f7), SkBits2Float(0x42a3adfe), SkBits2Float(0x415d43d0));
 path.lineTo(SkBits2Float(0x426ca531), SkBits2Float(0x411ff355));
@@ -4093,7 +4093,7 @@
 
 static void battleOp153(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41b12ed4), SkBits2Float(0xc2a60000), SkBits2Float(0x422d822c), SkBits2Float(0xc2944bde), SkBits2Float(0x426bdb91), SkBits2Float(0xc269a7f3));
@@ -4105,7 +4105,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a3adfe), SkBits2Float(0x415d43d0));
 path.cubicTo(SkBits2Float(0x42977493), SkBits2Float(0x42480062), SkBits2Float(0x423a617c), SkBits2Float(0x429bbd03), SkBits2Float(0x4123044a), SkBits2Float(0x42a4be9a));
 path.cubicTo(SkBits2Float(0xc1d1beaf), SkBits2Float(0x42adc030), SkBits2Float(0xc2750d30), SkBits2Float(0x4285e3a3), SkBits2Float(0xc2980208), SkBits2Float(0x42056911));
@@ -4122,7 +4122,7 @@
 
 static void battleOp154(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41bb5603), SkBits2Float(0xc2a60000), SkBits2Float(0x4236fa4e), SkBits2Float(0xc2923760), SkBits2Float(0x4275e892), SkBits2Float(0xc25f0dc8));
 path.cubicTo(SkBits2Float(0x429a6b6b), SkBits2Float(0xc219acd0), SkBits2Float(0x42a9c473), SkBits2Float(0xc173c3a6), SkBits2Float(0x42a5369d), SkBits2Float(0x410121d8));
@@ -4134,7 +4134,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5369e), SkBits2Float(0x410121d6));
 path.cubicTo(SkBits2Float(0x42a450b5), SkBits2Float(0x414aab85), SkBits2Float(0x42a2a6cd), SkBits2Float(0x4189bd6e), SkBits2Float(0x42a03d57), SkBits2Float(0x41ad66e6));
 path.lineTo(SkBits2Float(0x4267abf7), SkBits2Float(0x417ab39f));
@@ -4149,7 +4149,7 @@
 
 static void battleOp155(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41bb5603), SkBits2Float(0xc2a60000), SkBits2Float(0x4236fa4e), SkBits2Float(0xc2923760), SkBits2Float(0x4275e892), SkBits2Float(0xc25f0dc8));
@@ -4164,7 +4164,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a03d58), SkBits2Float(0x41ad66e7));
 path.cubicTo(SkBits2Float(0x428bedd4), SkBits2Float(0x426cda0a), SkBits2Float(0x420c6f35), SkBits2Float(0x42a955c4), SkBits2Float(0xc06f4c79), SkBits2Float(0x42a5d4d6));
 path.cubicTo(SkBits2Float(0xc22a58c2), SkBits2Float(0x42a253e8), SkBits2Float(0xc2960525), SkBits2Float(0x4252b394), SkBits2Float(0xc2a37db3), SkBits2Float(0x41660422));
@@ -4181,7 +4181,7 @@
 
 static void battleOp156(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c3ae1a), SkBits2Float(0xc2a60000), SkBits2Float(0x423eb2d3), SkBits2Float(0xc2906c00), SkBits2Float(0x427dc7c2), SkBits2Float(0xc2560e13));
 path.cubicTo(SkBits2Float(0x429e6e58), SkBits2Float(0xc20b4426), SkBits2Float(0x42abdf2b), SkBits2Float(0xc121d7a7), SkBits2Float(0x42a39f93), SkBits2Float(0x415fea21));
@@ -4193,7 +4193,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a39f93), SkBits2Float(0x415fea20));
 path.cubicTo(SkBits2Float(0x42a1ffad), SkBits2Float(0x4195f252), SkBits2Float(0x429f8ce1), SkBits2Float(0x41bb4c45), SkBits2Float(0x429c4e4c), SkBits2Float(0x41df969a));
 path.lineTo(SkBits2Float(0x4261fbff), SkBits2Float(0x41a1a14e));
@@ -4208,7 +4208,7 @@
 
 static void battleOp157(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c3ae1a), SkBits2Float(0xc2a60000), SkBits2Float(0x423eb2d3), SkBits2Float(0xc2906c00), SkBits2Float(0x427dc7c2), SkBits2Float(0xc2560e13));
@@ -4224,7 +4224,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429c4e4c), SkBits2Float(0x41df969b));
 path.cubicTo(SkBits2Float(0x4280e391), SkBits2Float(0x4284903f), SkBits2Float(0x41c7a851), SkBits2Float(0x42b2072e), SkBits2Float(0xc1713833), SkBits2Float(0x42a33d14));
 path.cubicTo(SkBits2Float(0xc25c7040), SkBits2Float(0x429472fb), SkBits2Float(0xc2a7bda2), SkBits2Float(0x421b8b2e), SkBits2Float(0xc2a5f5d6), SkBits2Float(0xbfe85110));
@@ -4241,7 +4241,7 @@
 
 static void battleOp158(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41cb677f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4245cb36), SkBits2Float(0xc28eb15b), SkBits2Float(0x42825fc2), SkBits2Float(0xc24d8299));
 path.cubicTo(SkBits2Float(0x42a1d9e8), SkBits2Float(0xc1fb44f8), SkBits2Float(0x42ad4967), SkBits2Float(0xc0aa7cf8), SkBits2Float(0x42a1679f), SkBits2Float(0x419b26cf));
@@ -4253,7 +4253,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a1679f), SkBits2Float(0x419b26d0));
 path.cubicTo(SkBits2Float(0x429f113c), SkBits2Float(0x41c20ede), SkBits2Float(0x429bdafe), SkBits2Float(0x41e80a2e), SkBits2Float(0x4297ceee), SkBits2Float(0x42065107));
 path.lineTo(SkBits2Float(0x425b7b5f), SkBits2Float(0x41c2314a));
@@ -4268,7 +4268,7 @@
 
 static void battleOp159(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41cb677f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4245cb36), SkBits2Float(0xc28eb15b), SkBits2Float(0x42825fc2), SkBits2Float(0xc24d8299));
@@ -4282,7 +4282,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4297ceef), SkBits2Float(0x42065107));
 path.cubicTo(SkBits2Float(0x426afc81), SkBits2Float(0x4290b9e3), SkBits2Float(0x4171c53f), SkBits2Float(0x42b7f2c1), SkBits2Float(0xc1ca446b), SkBits2Float(0x429e1c54));
 path.cubicTo(SkBits2Float(0xc2835add), SkBits2Float(0x428445e8), SkBits2Float(0xc2b3ab9e), SkBits2Float(0x41c6c009), SkBits2Float(0xc2a29b10), SkBits2Float(0xc18596e4));
@@ -4299,7 +4299,7 @@
 
 static void battleOp160(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d3ccce), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424d7252), SkBits2Float(0xc28cbd55), SkBits2Float(0x4285fbcc), SkBits2Float(0xc244010c));
 path.cubicTo(SkBits2Float(0x42a53e6e), SkBits2Float(0xc1dd0edd), SkBits2Float(0x42ae3d82), SkBits2Float(0xbdb630d0), SkBits2Float(0x429e3366), SkBits2Float(0x41c92323));
@@ -4311,7 +4311,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429e3367), SkBits2Float(0x41c92322));
 path.cubicTo(SkBits2Float(0x429b0cbc), SkBits2Float(0x41f0ca9b), SkBits2Float(0x4296f94f), SkBits2Float(0x420b9629), SkBits2Float(0x429206e2), SkBits2Float(0x421de34f));
 path.lineTo(SkBits2Float(0x42531f8a), SkBits2Float(0x41e4458f));
@@ -4326,7 +4326,7 @@
 
 static void battleOp161(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d3ccce), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424d7252), SkBits2Float(0xc28cbd55), SkBits2Float(0x4285fbcc), SkBits2Float(0xc244010c));
@@ -4340,7 +4340,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429206e2), SkBits2Float(0x421de34f));
 path.cubicTo(SkBits2Float(0x424fd7be), SkBits2Float(0x429cd433), SkBits2Float(0x40819da9), SkBits2Float(0x42bbf605), SkBits2Float(0xc20f7b98), SkBits2Float(0x4295b271));
 path.cubicTo(SkBits2Float(0xc2979573), SkBits2Float(0x425eddba), SkBits2Float(0xc2bb57fe), SkBits2Float(0x4109ef62), SkBits2Float(0xc2990315), SkBits2Float(0xc200bcbb));
@@ -4357,7 +4357,7 @@
 
 static void battleOp162(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41da3d7f), SkBits2Float(0xc2a60000), SkBits2Float(0x425345ee), SkBits2Float(0xc28b3082), SkBits2Float(0x4288a01b), SkBits2Float(0xc23c9177));
 path.cubicTo(SkBits2Float(0x42a79d3f), SkBits2Float(0xc1c583d9), SkBits2Float(0x42ae8eeb), SkBits2Float(0x407c6461), SkBits2Float(0x429b333a), SkBits2Float(0x41eb9731));
@@ -4369,7 +4369,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429b3339), SkBits2Float(0x41eb9733));
 path.cubicTo(SkBits2Float(0x429766b3), SkBits2Float(0x4209d0f3), SkBits2Float(0x4292a485), SkBits2Float(0x421d0e17), SkBits2Float(0x428cfdb5), SkBits2Float(0x422f3e33));
 path.lineTo(SkBits2Float(0x424bd7ac), SkBits2Float(0x41fd5d06));
@@ -4384,7 +4384,7 @@
 
 static void battleOp163(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41da3d7f), SkBits2Float(0xc2a60000), SkBits2Float(0x425345ee), SkBits2Float(0xc28b3082), SkBits2Float(0x4288a01b), SkBits2Float(0xc23c9177));
@@ -4398,7 +4398,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428cfdb5), SkBits2Float(0x422f3e36));
 path.cubicTo(SkBits2Float(0x42397b9c), SkBits2Float(0x42a54202), SkBits2Float(0xc0931849), SkBits2Float(0x42bd474f), SkBits2Float(0xc22e0fe8), SkBits2Float(0x428d5ab7));
 path.cubicTo(SkBits2Float(0xc2a4de63), SkBits2Float(0x423adc3f), SkBits2Float(0xc2bd50df), SkBits2Float(0xc08673c0), SkBits2Float(0xc28db7cd), SkBits2Float(0xc22ce1b4));
@@ -4415,7 +4415,7 @@
 
 static void battleOp164(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e183ec), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4259cec4), SkBits2Float(0xc2896274), SkBits2Float(0x428b79bc), SkBits2Float(0xc2340753));
 path.cubicTo(SkBits2Float(0x42aa0c16), SkBits2Float(0xc1aa937d), SkBits2Float(0x42ae7c71), SkBits2Float(0x41080a55), SkBits2Float(0x42974339), SkBits2Float(0x4208c1d5));
@@ -4427,7 +4427,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42974339), SkBits2Float(0x4208c1d6));
 path.cubicTo(SkBits2Float(0x4292b5f8), SkBits2Float(0x421ce537), SkBits2Float(0x428d2a3f), SkBits2Float(0x42301305), SkBits2Float(0x4286b52e), SkBits2Float(0x4242022c));
 path.lineTo(SkBits2Float(0x4242c218), SkBits2Float(0x420c3f43));
@@ -4442,7 +4442,7 @@
 
 static void battleOp165(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3725ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e183ec), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4259cec4), SkBits2Float(0xc2896274), SkBits2Float(0x428b79bc), SkBits2Float(0xc2340753));
@@ -4456,7 +4456,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4286b52e), SkBits2Float(0x4242022d));
 path.cubicTo(SkBits2Float(0x4245f9c6), SkBits2Float(0x42929b97), SkBits2Float(0x419b96e9), SkBits2Float(0x42ac9135), SkBits2Float(0xc12da222), SkBits2Float(0x42a4933a));
 path.cubicTo(SkBits2Float(0xc2249c85), SkBits2Float(0x429c9540), SkBits2Float(0xc2859c99), SkBits2Float(0x4267dd85), SkBits2Float(0xc29b4028), SkBits2Float(0x41eb0f05));
@@ -4475,7 +4475,7 @@
 
 static void battleOp166(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e5cd16), SkBits2Float(0xc2a60000), SkBits2Float(0x425da203), SkBits2Float(0xc2884b73), SkBits2Float(0x428d165b), SkBits2Float(0xc22eeec9));
 path.cubicTo(SkBits2Float(0x42ab5bb4), SkBits2Float(0xc19a8d5b), SkBits2Float(0x42ae3add), SkBits2Float(0x4132f7c2), SkBits2Float(0x4294adf4), SkBits2Float(0x4213a75b));
@@ -4487,7 +4487,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4294adf4), SkBits2Float(0x4213a75b));
 path.cubicTo(SkBits2Float(0x428facea), SkBits2Float(0x4227cf1b), SkBits2Float(0x4289a8e5), SkBits2Float(0x423ae500), SkBits2Float(0x4282b9a7), SkBits2Float(0x424c9dab));
 path.lineTo(SkBits2Float(0x423d0015), SkBits2Float(0x4213ea45));
@@ -4502,7 +4502,7 @@
 
 static void battleOp167(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e5cd16), SkBits2Float(0xc2a60000), SkBits2Float(0x425da203), SkBits2Float(0xc2884b73), SkBits2Float(0x428d165b), SkBits2Float(0xc22eeec9));
@@ -4516,7 +4516,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4282b9a8), SkBits2Float(0x424c9dac));
 path.cubicTo(SkBits2Float(0x4238a98e), SkBits2Float(0x42975dcd), SkBits2Float(0x416d9db4), SkBits2Float(0x42aecc7f), SkBits2Float(0xc17bb856), SkBits2Float(0x42a2fd9a));
 path.cubicTo(SkBits2Float(0xc2394396), SkBits2Float(0x42972eb6), SkBits2Float(0xc28e09e8), SkBits2Float(0x42543e5a), SkBits2Float(0xc29f69c3), SkBits2Float(0x41b9307a));
@@ -4535,7 +4535,7 @@
 
 static void battleOp168(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ea54b9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4261a7de), SkBits2Float(0xc2871f16), SkBits2Float(0x428ebc81), SkBits2Float(0xc2297f4d));
 path.cubicTo(SkBits2Float(0x42aca513), SkBits2Float(0xc18980da), SkBits2Float(0x42adc9a4), SkBits2Float(0x41604127), SkBits2Float(0x4291be57), SkBits2Float(0x421eee87));
@@ -4547,7 +4547,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4291be57), SkBits2Float(0x421eee8a));
 path.cubicTo(SkBits2Float(0x428c4169), SkBits2Float(0x42330feb), SkBits2Float(0x4285bd57), SkBits2Float(0x4246005c), SkBits2Float(0x427c99ac), SkBits2Float(0x4257723d));
 path.lineTo(SkBits2Float(0x42369a46), SkBits2Float(0x421bbe89));
@@ -4562,7 +4562,7 @@
 
 static void battleOp169(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3725ffa9), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ea54b9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4261a7de), SkBits2Float(0xc2871f16), SkBits2Float(0x428ebc81), SkBits2Float(0xc2297f4d));
@@ -4576,7 +4576,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427c99ad), SkBits2Float(0x4257723e));
 path.cubicTo(SkBits2Float(0x422a2459), SkBits2Float(0x429c0ff6), SkBits2Float(0x411ef0c1), SkBits2Float(0x42b0a109), SkBits2Float(0xc1a68a7f), SkBits2Float(0x42a0b1a2));
 path.cubicTo(SkBits2Float(0xc24e46af), SkBits2Float(0x4290c23b), SkBits2Float(0xc296269a), SkBits2Float(0x423e3c04), SkBits2Float(0xc2a2b82b), SkBits2Float(0x41835b51));
@@ -4595,7 +4595,7 @@
 
 static void battleOp170(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ef3488), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4265f5fc), SkBits2Float(0xc285d5a4), SkBits2Float(0x429072a6), SkBits2Float(0xc2239841));
 path.cubicTo(SkBits2Float(0x42adea4e), SkBits2Float(0xc16e14e5), SkBits2Float(0x42ad1da2), SkBits2Float(0x41886b20), SkBits2Float(0x428e5adb), SkBits2Float(0x422ac68e));
@@ -4607,7 +4607,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428e5adb), SkBits2Float(0x422ac690));
 path.cubicTo(SkBits2Float(0x42885732), SkBits2Float(0x423ed443), SkBits2Float(0x428148a8), SkBits2Float(0x42518e43), SkBits2Float(0x42729aa0), SkBits2Float(0x4262a4bd));
 path.lineTo(SkBits2Float(0x422f605c), SkBits2Float(0x4223d6b5));
@@ -4622,7 +4622,7 @@
 
 static void battleOp171(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ef3488), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4265f5fc), SkBits2Float(0xc285d5a4), SkBits2Float(0x429072a6), SkBits2Float(0xc2239841));
@@ -4636,7 +4636,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42729aa1), SkBits2Float(0x4262a4be));
 path.cubicTo(SkBits2Float(0x421a0aa1), SkBits2Float(0x42a0b8ab), SkBits2Float(0x4092ff14), SkBits2Float(0x42b1fc82), SkBits2Float(0xc1d17709), SkBits2Float(0x429d861f));
 path.cubicTo(SkBits2Float(0xc263d6eb), SkBits2Float(0x42890fbc), SkBits2Float(0xc29dea71), SkBits2Float(0x42253dbf), SkBits2Float(0xc2a5016a), SkBits2Float(0x4111261a));
@@ -4655,7 +4655,7 @@
 
 static void battleOp172(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f30c96), SkBits2Float(0xc2a60000), SkBits2Float(0x426956a5), SkBits2Float(0xc284cd4a), SkBits2Float(0x4291c05e), SkBits2Float(0xc21ee718));
 path.cubicTo(SkBits2Float(0x42aed56a), SkBits2Float(0xc150ce71), SkBits2Float(0x42ac7181), SkBits2Float(0x419b8107), SkBits2Float(0x428b8516), SkBits2Float(0x4233e422));
@@ -4667,7 +4667,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428b8516), SkBits2Float(0x4233e422));
 path.cubicTo(SkBits2Float(0x4285165c), SkBits2Float(0x4247d8d0), SkBits2Float(0x427b34bd), SkBits2Float(0x425a5d74), SkBits2Float(0x426a6401), SkBits2Float(0x426b20b1));
 path.lineTo(SkBits2Float(0x42297063), SkBits2Float(0x4229f8c9));
@@ -4682,7 +4682,7 @@
 
 static void battleOp173(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f30c96), SkBits2Float(0xc2a60000), SkBits2Float(0x426956a5), SkBits2Float(0xc284cd4a), SkBits2Float(0x4291c05e), SkBits2Float(0xc21ee718));
@@ -4697,7 +4697,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x426a6401), SkBits2Float(0x426b20b0));
 path.cubicTo(SkBits2Float(0x420d0644), SkBits2Float(0x42a419c2), SkBits2Float(0x3eb79d8f), SkBits2Float(0x42b29b69), SkBits2Float(0xc1f292a7), SkBits2Float(0x429a86c6));
 path.cubicTo(SkBits2Float(0xc27401e4), SkBits2Float(0x42827223), SkBits2Float(0xc2a34d81), SkBits2Float(0x4210aea0), SkBits2Float(0xc2a5dfaf), SkBits2Float(0x404f3106));
@@ -4716,7 +4716,7 @@
 
 static void battleOp174(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f67553), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426c5214), SkBits2Float(0xc283df7d), SkBits2Float(0x4292df93), SkBits2Float(0xc21ab724));
 path.cubicTo(SkBits2Float(0x42af961c), SkBits2Float(0xc136bd38), SkBits2Float(0x42abbe10), SkBits2Float(0x41ac5dd5), SkBits2Float(0x4288e395), SkBits2Float(0x423bcd53));
@@ -4728,7 +4728,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4288e396), SkBits2Float(0x423bcd52));
 path.cubicTo(SkBits2Float(0x42821571), SkBits2Float(0x424fa4b8), SkBits2Float(0x427470be), SkBits2Float(0x4261f24c), SkBits2Float(0x4262dfb6), SkBits2Float(0x4272637b));
 path.lineTo(SkBits2Float(0x42240156), SkBits2Float(0x422f387f));
@@ -4743,7 +4743,7 @@
 
 static void battleOp175(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f67553), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426c5214), SkBits2Float(0xc283df7d), SkBits2Float(0x4292df93), SkBits2Float(0xc21ab724));
@@ -4758,7 +4758,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4262dfb7), SkBits2Float(0x4272637c));
 path.cubicTo(SkBits2Float(0x4201435c), SkBits2Float(0x42a6e035), SkBits2Float(0xc05a052a), SkBits2Float(0x42b2d330), SkBits2Float(0xc207a774), SkBits2Float(0x429782c3));
 path.cubicTo(SkBits2Float(0xc280d74a), SkBits2Float(0x427864aa), SkBits2Float(0xc2a78489), SkBits2Float(0x41fbcc10), SkBits2Float(0xc2a5f467), SkBits2Float(0xbff86670));
@@ -4777,7 +4777,7 @@
 
 static void battleOp176(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f9cdf3), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426f3c43), SkBits2Float(0xc282f30b), SkBits2Float(0x4293f176), SkBits2Float(0xc2169536));
 path.cubicTo(SkBits2Float(0x42b044ca), SkBits2Float(0xc11d115b), SkBits2Float(0x42aaf59e), SkBits2Float(0x41bcd986), SkBits2Float(0x428633ff), SkBits2Float(0x42436703));
@@ -4789,7 +4789,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428633ff), SkBits2Float(0x42436705));
 path.cubicTo(SkBits2Float(0x427e0fd0), SkBits2Float(0x42571b29), SkBits2Float(0x426d975d), SkBits2Float(0x42692b9b), SkBits2Float(0x425b4ae0), SkBits2Float(0x427944c1));
 path.lineTo(SkBits2Float(0x421e8652), SkBits2Float(0x423431b3));
@@ -4804,7 +4804,7 @@
 
 static void battleOp177(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3743ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f9cdf3), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426f3c43), SkBits2Float(0xc282f30b), SkBits2Float(0x4293f176), SkBits2Float(0xc2169536));
@@ -4818,7 +4818,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x425b4ae0), SkBits2Float(0x427944c0));
 path.cubicTo(SkBits2Float(0x41eb12b8), SkBits2Float(0x42a964d5), SkBits2Float(0xc0e3546a), SkBits2Float(0x42b2bc1c), SkBits2Float(0xc2157060), SkBits2Float(0x42943ba4));
 path.cubicTo(SkBits2Float(0xc2873b19), SkBits2Float(0x426b7658), SkBits2Float(0xc2ab209f), SkBits2Float(0x41d60b1d), SkBits2Float(0xc2a5685b), SkBits2Float(0xc0e02f3c));
@@ -4837,7 +4837,7 @@
 
 static void battleOp178(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fc5f30), SkBits2Float(0xc2a5fffe), SkBits2Float(0x427176a0), SkBits2Float(0xc2823b95), SkBits2Float(0x4294be35), SkBits2Float(0xc21365c9));
 path.cubicTo(SkBits2Float(0x42b0c118), SkBits2Float(0xc1095198), SkBits2Float(0x42aa4b8f), SkBits2Float(0x41c9721a), SkBits2Float(0x42841312), SkBits2Float(0x42491ec0));
@@ -4849,7 +4849,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42841313), SkBits2Float(0x42491ebf));
 path.cubicTo(SkBits2Float(0x42793d8e), SkBits2Float(0x425cb36e), SkBits2Float(0x4268336d), SkBits2Float(0x426e9032), SkBits2Float(0x4255582b), SkBits2Float(0x427e60c5));
 path.lineTo(SkBits2Float(0x421a3990), SkBits2Float(0x4237e342));
@@ -4864,7 +4864,7 @@
 
 static void battleOp179(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fc5f30), SkBits2Float(0xc2a5fffe), SkBits2Float(0x427176a0), SkBits2Float(0xc2823b95), SkBits2Float(0x4294be35), SkBits2Float(0xc21365c9));
@@ -4878,7 +4878,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4255582a), SkBits2Float(0x427e60c6));
 path.cubicTo(SkBits2Float(0x41d8da26), SkBits2Float(0x42ab2f9f), SkBits2Float(0xc11f0392), SkBits2Float(0x42b2763a), SkBits2Float(0xc21fc8f1), SkBits2Float(0x4291829a));
 path.cubicTo(SkBits2Float(0xc28be87e), SkBits2Float(0x42611df4), SkBits2Float(0xc2ad8941), SkBits2Float(0x41b88f93), SkBits2Float(0xc2a49219), SkBits2Float(0xc12de56c));
@@ -4897,7 +4897,7 @@
 
 static void battleOp180(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fed5d1), SkBits2Float(0xc2a60000), SkBits2Float(0x4273981d), SkBits2Float(0xc28189e8), SkBits2Float(0x42957e40), SkBits2Float(0xc210547e));
 path.cubicTo(SkBits2Float(0x42b13073), SkBits2Float(0xc0eca961), SkBits2Float(0x42a99b35), SkBits2Float(0x41d57c6c), SkBits2Float(0x4281fa62), SkBits2Float(0x424e82d3));
@@ -4909,7 +4909,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4281fa62), SkBits2Float(0x424e82d5));
 path.cubicTo(SkBits2Float(0x4274817d), SkBits2Float(0x4261f5b7), SkBits2Float(0x4262ebfa), SkBits2Float(0x42739d02), SkBits2Float(0x424f88b8), SkBits2Float(0x428191ef));
 path.lineTo(SkBits2Float(0x4216064f), SkBits2Float(0x423b5489));
@@ -4924,7 +4924,7 @@
 
 static void battleOp181(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fed5d1), SkBits2Float(0xc2a60000), SkBits2Float(0x4273981d), SkBits2Float(0xc28189e8), SkBits2Float(0x42957e40), SkBits2Float(0xc210547e));
@@ -4938,7 +4938,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424f88ba), SkBits2Float(0x428191f0));
 path.cubicTo(SkBits2Float(0x41c732b7), SkBits2Float(0x42acca52), SkBits2Float(0xc14a7268), SkBits2Float(0x42b208b4), SkBits2Float(0xc22982dc), SkBits2Float(0x428ebb75));
 path.cubicTo(SkBits2Float(0xc2903490), SkBits2Float(0x4256dc6c), SkBits2Float(0xc2af8c6f), SkBits2Float(0x419be833), SkBits2Float(0xc2a36e37), SkBits2Float(0xc168c0a6));
@@ -4957,7 +4957,7 @@
 
 static void battleOp182(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420048ef), SkBits2Float(0xc2a60000), SkBits2Float(0x4275172d), SkBits2Float(0xc2810bd2), SkBits2Float(0x429602e3), SkBits2Float(0xc20e29dc));
 path.cubicTo(SkBits2Float(0x42b17a30), SkBits2Float(0xc0d1e0a1), SkBits2Float(0x42a9174e), SkBits2Float(0x41ddef9e), SkBits2Float(0x4280787d), SkBits2Float(0x4252400e));
@@ -4969,7 +4969,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4280787d), SkBits2Float(0x42524010));
 path.cubicTo(SkBits2Float(0x42711c0e), SkBits2Float(0x42659909), SkBits2Float(0x425f24ad), SkBits2Float(0x42771864), SkBits2Float(0x424b624a), SkBits2Float(0x4283347a));
 path.lineTo(SkBits2Float(0x42130648), SkBits2Float(0x423db1a5));
@@ -4984,7 +4984,7 @@
 
 static void battleOp183(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36d3ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420048ef), SkBits2Float(0xc2a60000), SkBits2Float(0x4275172d), SkBits2Float(0xc2810bd2), SkBits2Float(0x429602e3), SkBits2Float(0xc20e29dc));
@@ -4998,7 +4998,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424b624a), SkBits2Float(0x42833479));
 path.cubicTo(SkBits2Float(0x41baac2f), SkBits2Float(0x42adda12), SkBits2Float(0xc168f6a7), SkBits2Float(0x42b1a2b3), SkBits2Float(0xc2303c92), SkBits2Float(0x428cae5c));
 path.cubicTo(SkBits2Float(0xc2931dbe), SkBits2Float(0x424f7409), SkBits2Float(0xc2b0c9d8), SkBits2Float(0x41878abe), SkBits2Float(0xc2a26e7f), SkBits2Float(0xc188ef9a));
@@ -5017,7 +5017,7 @@
 
 static void battleOp184(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42011b87), SkBits2Float(0xc2a5fffe), SkBits2Float(0x427681ab), SkBits2Float(0xc280937a), SkBits2Float(0x42967eb3), SkBits2Float(0xc20c1a94));
 path.cubicTo(SkBits2Float(0x42b1bc91), SkBits2Float(0xc0b87191), SkBits2Float(0x42a89454), SkBits2Float(0x41e5ed6f), SkBits2Float(0x427e0902), SkBits2Float(0x4255c0a2));
@@ -5029,7 +5029,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427e0901), SkBits2Float(0x4255c0a4));
 path.cubicTo(SkBits2Float(0x426dd77c), SkBits2Float(0x4268ff65), SkBits2Float(0x425b838b), SkBits2Float(0x427a571f), SkBits2Float(0x42476779), SkBits2Float(0x4284b92f));
 path.lineTo(SkBits2Float(0x421025c9), SkBits2Float(0x423fe3a3));
@@ -5044,7 +5044,7 @@
 
 static void battleOp185(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42011b87), SkBits2Float(0xc2a5fffe), SkBits2Float(0x427681ab), SkBits2Float(0xc280937a), SkBits2Float(0x42967eb3), SkBits2Float(0xc20c1a94));
@@ -5060,7 +5060,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42476779), SkBits2Float(0x4284b92f));
 path.cubicTo(SkBits2Float(0x41aeb99d), SkBits2Float(0x42aece6d), SkBits2Float(0xc182ebc7), SkBits2Float(0x42b12f04), SkBits2Float(0xc236847b), SkBits2Float(0x428aaa1d));
 path.cubicTo(SkBits2Float(0xc295c989), SkBits2Float(0x42484a6d), SkBits2Float(0xc2b1d401), SkBits2Float(0x41683386), SkBits2Float(0xc2a15607), SkBits2Float(0xc19c4a77));
@@ -5079,7 +5079,7 @@
 
 static void battleOp186(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4201bd60), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427797bb), SkBits2Float(0xc2803682), SkBits2Float(0x4296dc8c), SkBits2Float(0xc20a848f));
 path.cubicTo(SkBits2Float(0x42b1ed3b), SkBits2Float(0xc0a4e0c3), SkBits2Float(0x42a82bcd), SkBits2Float(0x41ec0db8), SkBits2Float(0x427bc56e), SkBits2Float(0x42586a20));
@@ -5091,7 +5091,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427bc56c), SkBits2Float(0x42586a22));
 path.cubicTo(SkBits2Float(0x426b4cc6), SkBits2Float(0x426b93ad), SkBits2Float(0x4258b1e1), SkBits2Float(0x427ccbca), SkBits2Float(0x42445140), SkBits2Float(0x4285de6e));
 path.lineTo(SkBits2Float(0x420dea8b), SkBits2Float(0x42418b9b));
@@ -5106,7 +5106,7 @@
 
 static void battleOp187(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb69400ae), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4201bd60), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427797bb), SkBits2Float(0xc2803682), SkBits2Float(0x4296dc8c), SkBits2Float(0xc20a848f));
@@ -5124,7 +5124,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42445140), SkBits2Float(0x4285de6e));
 path.cubicTo(SkBits2Float(0x41a5801a), SkBits2Float(0x42af8153), SkBits2Float(0xc18dfe3b), SkBits2Float(0x42b0c99d), SkBits2Float(0xc23b472e), SkBits2Float(0x42891183));
 path.cubicTo(SkBits2Float(0xc297c79f), SkBits2Float(0x4242b2d1), SkBits2Float(0xc2b28961), SkBits2Float(0x414a2ba6), SkBits2Float(0xc2a0659f), SkBits2Float(0xc1ab0f22));
@@ -5143,7 +5143,7 @@
 
 static void battleOp188(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42025498), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42789b1b), SkBits2Float(0xc27fbe84), SkBits2Float(0x42973334), SkBits2Float(0xc2090897));
 path.cubicTo(SkBits2Float(0x42b218da), SkBits2Float(0xc092954a), SkBits2Float(0x42a7c71a), SkBits2Float(0x41f1c3b5), SkBits2Float(0x4279a1de), SkBits2Float(0x425ae0d9));
@@ -5155,7 +5155,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4279a1de), SkBits2Float(0x425ae0d9));
 path.cubicTo(SkBits2Float(0x4268e6ce), SkBits2Float(0x426df5b7), SkBits2Float(0x425609c8), SkBits2Float(0x427f0f64), SkBits2Float(0x42416967), SkBits2Float(0x4286ec0f));
 path.lineTo(SkBits2Float(0x420bd0d2), SkBits2Float(0x42431170));
@@ -5170,7 +5170,7 @@
 
 static void battleOp189(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7240057), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42025498), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42789b1b), SkBits2Float(0xc27fbe84), SkBits2Float(0x42973334), SkBits2Float(0xc2090897));
@@ -5185,7 +5185,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42416967), SkBits2Float(0x4286ec0f));
 path.cubicTo(SkBits2Float(0x419cd99a), SkBits2Float(0x42b02173), SkBits2Float(0xc19850b8), SkBits2Float(0x42b06117), SkBits2Float(0xc23fac11), SkBits2Float(0x42878a96));
 path.cubicTo(SkBits2Float(0xc29997e3), SkBits2Float(0x423d682a), SkBits2Float(0xc2b3208c), SkBits2Float(0x412e025f), SkBits2Float(0xc29f71a3), SkBits2Float(0xc1b8c415));
@@ -5204,7 +5204,7 @@
 
 static void battleOp190(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202b56e), SkBits2Float(0xc2a60000), SkBits2Float(0x427940ff), SkBits2Float(0xc27f4e67), SkBits2Float(0x42976a2d), SkBits2Float(0xc20814ff));
 path.cubicTo(SkBits2Float(0x42b233da), SkBits2Float(0xc086dcb5), SkBits2Float(0x42a78518), SkBits2Float(0x41f56a27), SkBits2Float(0x42784037), SkBits2Float(0x425c71a4));
@@ -5216,7 +5216,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42784038), SkBits2Float(0x425c71a4));
 path.cubicTo(SkBits2Float(0x42675aa4), SkBits2Float(0x426f78d5), SkBits2Float(0x4254535c), SkBits2Float(0x42803f48), SkBits2Float(0x423f8a54), SkBits2Float(0x4287967e));
 path.lineTo(SkBits2Float(0x420a7682), SkBits2Float(0x424407da));
@@ -5231,7 +5231,7 @@
 
 static void battleOp191(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202b56e), SkBits2Float(0xc2a60000), SkBits2Float(0x427940ff), SkBits2Float(0xc27f4e67), SkBits2Float(0x42976a2d), SkBits2Float(0xc20814ff));
@@ -5246,7 +5246,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423f8a55), SkBits2Float(0x4287967f));
 path.cubicTo(SkBits2Float(0x41974ba2), SkBits2Float(0x42b0846d), SkBits2Float(0xc19ee9a3), SkBits2Float(0x42b01937), SkBits2Float(0xc2427547), SkBits2Float(0x42868bae));
 path.cubicTo(SkBits2Float(0xc29abade), SkBits2Float(0x4239fc4c), SkBits2Float(0xc2b3780d), SkBits2Float(0x411bee16), SkBits2Float(0xc29ecbab), SkBits2Float(0xc1c17e4f));
@@ -5265,7 +5265,7 @@
 
 static void battleOp192(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202fa25), SkBits2Float(0xc2a60000), SkBits2Float(0x4279b699), SkBits2Float(0xc27efea4), SkBits2Float(0x429790ee), SkBits2Float(0xc20767f9));
 path.cubicTo(SkBits2Float(0x42b24690), SkBits2Float(0xc07d14fa), SkBits2Float(0x42a75587), SkBits2Float(0x41f80076), SkBits2Float(0x427743d2), SkBits2Float(0x425d8c9b));
@@ -5277,7 +5277,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427743d4), SkBits2Float(0x425d8c98));
 path.cubicTo(SkBits2Float(0x4266401a), SkBits2Float(0x427089e5), SkBits2Float(0x42531ae2), SkBits2Float(0x4280c0a0), SkBits2Float(0x423e3514), SkBits2Float(0x42880e64));
 path.lineTo(SkBits2Float(0x42097fd1), SkBits2Float(0x4244b531));
@@ -5291,7 +5291,7 @@
 
 static void battleOp193(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e15a675), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e95a67a), SkBits2Float(0xc2a5ffcd), SkBits2Float(0x3ee07980), SkBits2Float(0xc2a5ff68));
 path.lineTo(SkBits2Float(0x3ea245bb), SkBits2Float(0xc26fff25));
@@ -5301,7 +5301,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee07a10), SkBits2Float(0xc2a5ff68));
 path.cubicTo(SkBits2Float(0x3ee7f565), SkBits2Float(0xc2a5ff5d), SkBits2Float(0x3eef70d9), SkBits2Float(0xc2a5ff52), SkBits2Float(0x3ef6ec4d), SkBits2Float(0xc2a5ff47));
 path.lineTo(SkBits2Float(0x3eb27fdb), SkBits2Float(0xc26ffef6));
@@ -5316,7 +5316,7 @@
 
 static void battleOp194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3691e768), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e15a675), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e95a67a), SkBits2Float(0xc2a5ffcd), SkBits2Float(0x3ee07a10), SkBits2Float(0xc2a5ff68));
@@ -5328,7 +5328,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ef6ec9b), SkBits2Float(0xc2a5ff48));
 path.cubicTo(SkBits2Float(0x3f3816c9), SkBits2Float(0xc2a5fe94), SkBits2Float(0x3f74b6e1), SkBits2Float(0xc2a5fd5b), SkBits2Float(0x3f98ab0b), SkBits2Float(0xc2a5fb9d));
 path.lineTo(SkBits2Float(0x3f5cb973), SkBits2Float(0xc26ff9a8));
@@ -5343,7 +5343,7 @@
 
 static void battleOp195(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f0607d9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3f860760), SkBits2Float(0xc2a5fd76), SkBits2Float(0x3fc90825), SkBits2Float(0xc2a5f863));
 path.lineTo(SkBits2Float(0x3f9152f7), SkBits2Float(0xc26ff500));
@@ -5353,7 +5353,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fc9081a), SkBits2Float(0xc2a5f864));
 path.cubicTo(SkBits2Float(0x3fcfbb75), SkBits2Float(0xc2a5f7e2), SkBits2Float(0x3fd66eab), SkBits2Float(0xc2a5f75a), SkBits2Float(0x3fdd21d8), SkBits2Float(0xc2a5f6cb));
 path.lineTo(SkBits2Float(0x3f9fdac0), SkBits2Float(0xc26ff2b1));
@@ -5368,14 +5368,14 @@
 
 static void battleOp196(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36a51f4a), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3f0607d1), SkBits2Float(0xc2a60000), SkBits2Float(0x3f860758), SkBits2Float(0xc2a5fd76), SkBits2Float(0x3fc9081a), SkBits2Float(0xc2a5f864));
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3fdd21ce), SkBits2Float(0xc2a5f6cb));
 path.cubicTo(SkBits2Float(0x4024daa1), SkBits2Float(0xc2a5edc0), SkBits2Float(0x405b1f05), SkBits2Float(0xc2a5de0d), SkBits2Float(0x4088aca3), SkBits2Float(0xc2a5c7b3));
 path.lineTo(SkBits2Float(0x40459a01), SkBits2Float(0xc26fae99));
@@ -5390,7 +5390,7 @@
 
 static void battleOp197(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fa0bd52), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4020babd), SkBits2Float(0xc2a5f168), SkBits2Float(0x40710446), SkBits2Float(0xc2a5d43c));
 path.lineTo(SkBits2Float(0x402e3a94), SkBits2Float(0xc26fc0ba));
@@ -5400,7 +5400,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4071043c), SkBits2Float(0xc2a5d43c));
 path.cubicTo(SkBits2Float(0x40790b78), SkBits2Float(0xc2a5d151), SkBits2Float(0x40808943), SkBits2Float(0xc2a5ce41), SkBits2Float(0x40848cac), SkBits2Float(0xc2a5cb0c));
 path.lineTo(SkBits2Float(0x403fa34c), SkBits2Float(0xc26fb371));
@@ -5415,7 +5415,7 @@
 
 static void battleOp198(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x369bbf59), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3fa0bd4b), SkBits2Float(0xc2a60000), SkBits2Float(0x4020bab6), SkBits2Float(0xc2a5f168), SkBits2Float(0x4071043c), SkBits2Float(0xc2a5d43c));
@@ -5429,7 +5429,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40848cae), SkBits2Float(0xc2a5cb0c));
 path.cubicTo(SkBits2Float(0x40c597bc), SkBits2Float(0xc2a5970c), SkBits2Float(0x41033f43), SkBits2Float(0xc2a53cca), SkBits2Float(0x41238fb3), SkBits2Float(0xc2a4bc74));
 path.lineTo(SkBits2Float(0x40ec7963), SkBits2Float(0xc26e2c38));
@@ -5444,7 +5444,7 @@
 
 static void battleOp199(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ffdfad4), SkBits2Float(0xc2a60000), SkBits2Float(0x407df074), SkBits2Float(0xc2a5db92), SkBits2Float(0x40be4d32), SkBits2Float(0xc2a592c7));
 path.lineTo(SkBits2Float(0x40899143), SkBits2Float(0xc26f6217));
@@ -5454,7 +5454,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40be4d37), SkBits2Float(0xc2a592c7));
 path.cubicTo(SkBits2Float(0x40c4a257), SkBits2Float(0xc2a58b80), SkBits2Float(0x40caf70c), SkBits2Float(0xc2a583db), SkBits2Float(0x40d14b4e), SkBits2Float(0xc2a57bda));
 path.lineTo(SkBits2Float(0x40974c04), SkBits2Float(0xc26f40f2));
@@ -5469,7 +5469,7 @@
 
 static void battleOp200(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3673fea3), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3ffdfad4), SkBits2Float(0xc2a60000), SkBits2Float(0x407df074), SkBits2Float(0xc2a5db92), SkBits2Float(0x40be4d37), SkBits2Float(0xc2a592c7));
@@ -5482,7 +5482,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x40d14b4a), SkBits2Float(0xc2a57bdb));
 path.cubicTo(SkBits2Float(0x411bf161), SkBits2Float(0xc2a4fa1a), SkBits2Float(0x414ef5ad), SkBits2Float(0xc2a4190e), SkBits2Float(0x4180b83e), SkBits2Float(0xc2a2d9dc));
 path.lineTo(SkBits2Float(0x413a19cf), SkBits2Float(0xc26b727f));
@@ -5497,7 +5497,7 @@
 
 static void battleOp201(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4059d383), SkBits2Float(0xc2a5ffff), SkBits2Float(0x40d9b918), SkBits2Float(0xc2a594d0), SkBits2Float(0x4122e820), SkBits2Float(0xc2a4bf0c));
 path.lineTo(SkBits2Float(0x40eb871c), SkBits2Float(0xc26e2ff8));
@@ -5507,7 +5507,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4122e81e), SkBits2Float(0xc2a4bf0c));
 path.cubicTo(SkBits2Float(0x41284f3c), SkBits2Float(0xc2a4a9ac), SkBits2Float(0x412db549), SkBits2Float(0xc2a4933e), SkBits2Float(0x41331a33), SkBits2Float(0xc2a47bbf));
 path.lineTo(SkBits2Float(0x410178be), SkBits2Float(0xc26dceac));
@@ -5522,7 +5522,7 @@
 
 static void battleOp202(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4059d380), SkBits2Float(0xc2a60000), SkBits2Float(0x40d9b915), SkBits2Float(0xc2a594d0), SkBits2Float(0x4122e81e), SkBits2Float(0xc2a4bf0c));
@@ -5536,7 +5536,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41331a39), SkBits2Float(0xc2a47bc0));
 path.cubicTo(SkBits2Float(0x41854b40), SkBits2Float(0xc2a2feb5), SkBits2Float(0x41b05576), SkBits2Float(0xc2a06b6c), SkBits2Float(0x41da0834), SkBits2Float(0xc29ccbb1));
 path.lineTo(SkBits2Float(0x419d9d10), SkBits2Float(0xc262b148));
@@ -5551,7 +5551,7 @@
 
 static void battleOp203(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4087af55), SkBits2Float(0xc2a5ffff), SkBits2Float(0x410795c5), SkBits2Float(0xc2a559a4), SkBits2Float(0x414aa20a), SkBits2Float(0xc2a40e63));
 path.lineTo(SkBits2Float(0x41127b4b), SkBits2Float(0xc26d308f));
@@ -5561,7 +5561,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x414aa206), SkBits2Float(0xc2a40e63));
 path.cubicTo(SkBits2Float(0x4151559c), SkBits2Float(0xc2a3ed46), SkBits2Float(0x41580726), SkBits2Float(0xc2a3ca86), SkBits2Float(0x415eb67b), SkBits2Float(0xc2a3a622));
 path.lineTo(SkBits2Float(0x4120ff4d), SkBits2Float(0xc26c99d6));
@@ -5576,7 +5576,7 @@
 
 static void battleOp204(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4087af52), SkBits2Float(0xc2a60000), SkBits2Float(0x410795c2), SkBits2Float(0xc2a559a4), SkBits2Float(0x414aa206), SkBits2Float(0xc2a40e63));
@@ -5590,7 +5590,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x415eb680), SkBits2Float(0xc2a3a623));
 path.cubicTo(SkBits2Float(0x41a59721), SkBits2Float(0xc2a157ad), SkBits2Float(0x41da77ab), SkBits2Float(0xc29d5c25), SkBits2Float(0x420662d7), SkBits2Float(0xc297cafd));
 path.lineTo(SkBits2Float(0x41c24b0d), SkBits2Float(0xc25b75ac));
@@ -5605,7 +5605,7 @@
 
 static void battleOp205(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40a2e582), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4122b94f), SkBits2Float(0xc2a51039), SkBits2Float(0x4172cca0), SkBits2Float(0xc2a333b4));
 path.lineTo(SkBits2Float(0x412f847d), SkBits2Float(0xc26bf464));
@@ -5615,7 +5615,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4172cc9b), SkBits2Float(0xc2a333b4));
 path.cubicTo(SkBits2Float(0x417acd1a), SkBits2Float(0xc2a30415), SkBits2Float(0x41816508), SkBits2Float(0xc2a2d21d), SkBits2Float(0x4185619b), SkBits2Float(0xc2a29dcb));
 path.lineTo(SkBits2Float(0x4140d724), SkBits2Float(0xc26b1ba8));
@@ -5630,7 +5630,7 @@
 
 static void battleOp206(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36b5ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40a2e57f), SkBits2Float(0xc2a60000), SkBits2Float(0x4122b94c), SkBits2Float(0xc2a51039), SkBits2Float(0x4172cc9b), SkBits2Float(0xc2a333b4));
@@ -5644,7 +5644,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4185619b), SkBits2Float(0xc2a29dcc));
 path.cubicTo(SkBits2Float(0x41c61a92), SkBits2Float(0xc29f4c69), SkBits2Float(0x42023dd6), SkBits2Float(0xc299958f), SkBits2Float(0x421f3a98), SkBits2Float(0xc291a994));
 path.lineTo(SkBits2Float(0x41e635e1), SkBits2Float(0xc25298a5));
@@ -5659,7 +5659,7 @@
 
 static void battleOp207(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40c39389), SkBits2Float(0xc2a60000), SkBits2Float(0x414346f4), SkBits2Float(0xc2a4a65f), SkBits2Float(0x419158cf), SkBits2Float(0xc2a1f965));
 path.lineTo(SkBits2Float(0x415223e0), SkBits2Float(0xc26a2df8));
@@ -5669,7 +5669,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x419158d0), SkBits2Float(0xc2a1f965));
 path.cubicTo(SkBits2Float(0x41961cea), SkBits2Float(0xc2a1b4f6), SkBits2Float(0x419addf6), SkBits2Float(0xc2a16d2c), SkBits2Float(0x419f9bbb), SkBits2Float(0xc2a12207));
 path.lineTo(SkBits2Float(0x4166c251), SkBits2Float(0xc268f69a));
@@ -5684,7 +5684,7 @@
 
 static void battleOp208(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40c39389), SkBits2Float(0xc2a60000), SkBits2Float(0x414346f4), SkBits2Float(0xc2a4a65f), SkBits2Float(0x419158d0), SkBits2Float(0xc2a1f965));
@@ -5696,7 +5696,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x419f9bbc), SkBits2Float(0xc2a12208));
 path.cubicTo(SkBits2Float(0x41eca53e), SkBits2Float(0xc29c5d1a), SkBits2Float(0x421ad1be), SkBits2Float(0xc2942e2b), SkBits2Float(0x423b8fe1), SkBits2Float(0xc288f8a3));
 path.lineTo(SkBits2Float(0x42079647), SkBits2Float(0xc24607dc));
@@ -5711,7 +5711,7 @@
 
 static void battleOp209(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40e86425), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4167e385), SkBits2Float(0xc2a41801), SkBits2Float(0x41ac0ecd), SkBits2Float(0xc2a05484));
 path.lineTo(SkBits2Float(0x4178c21d), SkBits2Float(0xc267cd79));
@@ -5721,7 +5721,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41ac0ecb), SkBits2Float(0xc2a05485));
 path.cubicTo(SkBits2Float(0x41b1a941), SkBits2Float(0xc29ff44e), SkBits2Float(0x41b73ea0), SkBits2Float(0xc29f8f65), SkBits2Float(0x41bcce84), SkBits2Float(0xc29f25d1));
 path.lineTo(SkBits2Float(0x41887c9d), SkBits2Float(0xc26617d6));
@@ -5736,7 +5736,7 @@
 
 static void battleOp210(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3673fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x40e86421), SkBits2Float(0xc2a60000), SkBits2Float(0x4167e381), SkBits2Float(0xc2a41801), SkBits2Float(0x41ac0eca), SkBits2Float(0xc2a05484));
@@ -5751,7 +5751,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41bcce83), SkBits2Float(0xc29f25d2));
 path.cubicTo(SkBits2Float(0x420ba3b4), SkBits2Float(0xc2987080), SkBits2Float(0x42357f09), SkBits2Float(0xc28cfcb1), SkBits2Float(0x42592f07), SkBits2Float(0xc27b1ba7));
 path.lineTo(SkBits2Float(0x421d0012), SkBits2Float(0xc235861c));
@@ -5766,7 +5766,7 @@
 
 static void battleOp211(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x411e5541), SkBits2Float(0xc2a5ffff), SkBits2Float(0x419db1ee), SkBits2Float(0xc2a275ef), SkBits2Float(0x41e7e0a3), SkBits2Float(0xc29b8c98));
 path.lineTo(SkBits2Float(0x41a79f51), SkBits2Float(0xc260e3f1));
@@ -5776,7 +5776,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41e7e0a8), SkBits2Float(0xc29b8c98));
 path.cubicTo(SkBits2Float(0x41ef46bb), SkBits2Float(0xc29adc20), SkBits2Float(0x41f6a013), SkBits2Float(0xc29a2338), SkBits2Float(0x41fdebc8), SkBits2Float(0xc29961f8));
 path.lineTo(SkBits2Float(0x41b78eb0), SkBits2Float(0xc25dc215));
@@ -5791,7 +5791,7 @@
 
 static void battleOp212(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea3), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x411e5541), SkBits2Float(0xc2a5ffff), SkBits2Float(0x419db1ee), SkBits2Float(0xc2a275ef), SkBits2Float(0x41e7e0a8), SkBits2Float(0xc29b8c98));
@@ -5803,7 +5803,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41fdebc9), SkBits2Float(0xc29961f9));
 path.cubicTo(SkBits2Float(0x423a7ccd), SkBits2Float(0xc28d1085), SkBits2Float(0x426d8f8d), SkBits2Float(0xc270b4b0), SkBits2Float(0x4288fa0c), SkBits2Float(0xc23b8bbf));
 path.lineTo(SkBits2Float(0x424609e8), SkBits2Float(0xc207934a));
@@ -5818,7 +5818,7 @@
 
 static void battleOp213(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4151cd59), SkBits2Float(0xc2a5ffff), SkBits2Float(0x41d04f3f), SkBits2Float(0xc29fc954), SkBits2Float(0x4216e058), SkBits2Float(0xc293de54));
 path.lineTo(SkBits2Float(0x41da226b), SkBits2Float(0xc255c926));
@@ -5828,7 +5828,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4216e057), SkBits2Float(0xc293de54));
 path.cubicTo(SkBits2Float(0x421b86ea), SkBits2Float(0xc292aea0), SkBits2Float(0x42201eff), SkBits2Float(0xc29170ed), SkBits2Float(0x4224a79b), SkBits2Float(0xc290257e));
 path.lineTo(SkBits2Float(0x41ee0e15), SkBits2Float(0xc2506790));
@@ -5843,7 +5843,7 @@
 
 static void battleOp214(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4151cd58), SkBits2Float(0xc2a60000), SkBits2Float(0x41d04f3d), SkBits2Float(0xc29fc954), SkBits2Float(0x4216e057), SkBits2Float(0xc293de54));
@@ -5857,7 +5857,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4224a79b), SkBits2Float(0xc290257f));
 path.cubicTo(SkBits2Float(0x426f06c3), SkBits2Float(0xc275d105), SkBits2Float(0x42930d85), SkBits2Float(0xc2303df6), SkBits2Float(0x429f3103), SkBits2Float(0xc1bc373f));
 path.lineTo(SkBits2Float(0x42662806), SkBits2Float(0xc1880f44));
@@ -5872,7 +5872,7 @@
 
 static void battleOp215(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41741cf0), SkBits2Float(0xc2a60000), SkBits2Float(0x41f1c060), SkBits2Float(0xc29d96da), SkBits2Float(0x422cf7a2), SkBits2Float(0xc28db11c));
 path.lineTo(SkBits2Float(0x41fa12be), SkBits2Float(0xc24cdb0d));
@@ -5882,7 +5882,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x422cf7a1), SkBits2Float(0xc28db11c));
 path.cubicTo(SkBits2Float(0x423224e7), SkBits2Float(0xc28c1ca8), SkBits2Float(0x42373bc3), SkBits2Float(0xc28a7620), SkBits2Float(0x423c3abd), SkBits2Float(0xc288bdfd));
 path.lineTo(SkBits2Float(0x420811ca), SkBits2Float(0xc245b313));
@@ -5897,7 +5897,7 @@
 
 static void battleOp216(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41741cef), SkBits2Float(0xc2a60000), SkBits2Float(0x41f1c05e), SkBits2Float(0xc29d96da), SkBits2Float(0x422cf7a1), SkBits2Float(0xc28db11c));
@@ -5911,7 +5911,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423c3abe), SkBits2Float(0xc288bdfe));
 path.cubicTo(SkBits2Float(0x42874551), SkBits2Float(0xc258d4f5), SkBits2Float(0x42a17ace), SkBits2Float(0xc1fc3ce7), SkBits2Float(0x42a57844), SkBits2Float(0xc0d41d22));
 path.lineTo(SkBits2Float(0x426f3bc1), SkBits2Float(0xc09955d3));
@@ -5926,7 +5926,7 @@
 
 static void battleOp217(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4188e880), SkBits2Float(0xc2a60000), SkBits2Float(0x42073c1a), SkBits2Float(0xc29b6b86), SkBits2Float(0x423f3295), SkBits2Float(0xc287b573));
 path.lineTo(SkBits2Float(0x420a3712), SkBits2Float(0xc2443499));
@@ -5936,7 +5936,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423f3294), SkBits2Float(0xc287b572));
 path.cubicTo(SkBits2Float(0x4244c015), SkBits2Float(0xc285c0c3), SkBits2Float(0x424a2e84), SkBits2Float(0xc283b664), SkBits2Float(0x424f7bec), SkBits2Float(0xc281970f));
 path.lineTo(SkBits2Float(0x4215fd0e), SkBits2Float(0xc23b5bf1));
@@ -5951,7 +5951,7 @@
 
 static void battleOp218(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4188e880), SkBits2Float(0xc2a60000), SkBits2Float(0x42073c1a), SkBits2Float(0xc29b6b86), SkBits2Float(0x423f3295), SkBits2Float(0xc287b573));
@@ -5964,7 +5964,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424f7bed), SkBits2Float(0xc281970f));
 path.cubicTo(SkBits2Float(0x42939bdb), SkBits2Float(0xc23cf22a), SkBits2Float(0x42aabb70), SkBits2Float(0xc19e30f8), SkBits2Float(0x42a530dd), SkBits2Float(0x4102f5b1));
 path.lineTo(SkBits2Float(0x426ed486), SkBits2Float(0x40bd56e4));
@@ -5979,7 +5979,7 @@
 
 static void battleOp219(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4198fc97), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4216a3e3), SkBits2Float(0xc298caff), SkBits2Float(0x4251e7a7), SkBits2Float(0xc2809c9b));
 path.lineTo(SkBits2Float(0x4217bd0d), SkBits2Float(0xc239f1d8));
@@ -5989,7 +5989,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4251e7a7), SkBits2Float(0xc2809c9c));
 path.cubicTo(SkBits2Float(0x4257c623), SkBits2Float(0xc27c6f1e), SkBits2Float(0x425d7a38), SkBits2Float(0xc27771f7), SkBits2Float(0x42630157), SkBits2Float(0xc27243fd));
 path.lineTo(SkBits2Float(0x422419a4), SkBits2Float(0xc22f21bb));
@@ -6004,7 +6004,7 @@
 
 static void battleOp220(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4198fc97), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4216a3e3), SkBits2Float(0xc298caff), SkBits2Float(0x4251e7a7), SkBits2Float(0xc2809c9c));
@@ -6016,7 +6016,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42630157), SkBits2Float(0xc27243ff));
 path.cubicTo(SkBits2Float(0x429f78af), SkBits2Float(0xc21c1e80), SkBits2Float(0x42b11918), SkBits2Float(0xc0cad7ee), SkBits2Float(0x429f0274), SkBits2Float(0x41bea8f4));
 path.lineTo(SkBits2Float(0x4265e4b4), SkBits2Float(0x4189d394));
@@ -6031,7 +6031,7 @@
 
 static void battleOp221(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ae0130), SkBits2Float(0xc2a5ffff), SkBits2Float(0x422a8737), SkBits2Float(0xc294ec91), SkBits2Float(0x42689b67), SkBits2Float(0xc26ce46c));
 path.lineTo(SkBits2Float(0x42282651), SkBits2Float(0xc22b3f58));
@@ -6041,7 +6041,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42689b68), SkBits2Float(0xc26ce46d));
 path.cubicTo(SkBits2Float(0x426ebcd2), SkBits2Float(0xc266df67), SkBits2Float(0x4274a1d2), SkBits2Float(0xc2609e09), SkBits2Float(0x427a4701), SkBits2Float(0xc25a23f2));
 path.lineTo(SkBits2Float(0x4234ec64), SkBits2Float(0xc21db11e));
@@ -6056,7 +6056,7 @@
 
 static void battleOp222(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ae0130), SkBits2Float(0xc2a5ffff), SkBits2Float(0x422a8737), SkBits2Float(0xc294ec91), SkBits2Float(0x42689b68), SkBits2Float(0xc26ce46d));
@@ -6068,7 +6068,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427a4702), SkBits2Float(0xc25a23f2));
 path.cubicTo(SkBits2Float(0x42ac7185), SkBits2Float(0xc1db2f83), SkBits2Float(0x42b35ed0), SkBits2Float(0x413e447a), SkBits2Float(0x428e4a3d), SkBits2Float(0x422afde8));
 path.lineTo(SkBits2Float(0x424db871), SkBits2Float(0x41f73799));
@@ -6083,7 +6083,7 @@
 
 static void battleOp223(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c50a2c), SkBits2Float(0xc2a60000), SkBits2Float(0x423ff37f), SkBits2Float(0xc2901f4e), SkBits2Float(0x427f077c), SkBits2Float(0xc25490c6));
 path.lineTo(SkBits2Float(0x42385bc5), SkBits2Float(0xc219a96d));
@@ -6093,7 +6093,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427f077b), SkBits2Float(0xc25490c6));
 path.cubicTo(SkBits2Float(0x42829e52), SkBits2Float(0xc24d1e28), SkBits2Float(0x42858ec1), SkBits2Float(0xc24566d6), SkBits2Float(0x428852e3), SkBits2Float(0xc23d7081));
 path.lineTo(SkBits2Float(0x42451839), SkBits2Float(0xc208f1b7));
@@ -6108,7 +6108,7 @@
 
 static void battleOp224(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c50a2c), SkBits2Float(0xc2a60000), SkBits2Float(0x423ff37f), SkBits2Float(0xc2901f4e), SkBits2Float(0x427f077c), SkBits2Float(0xc25490c6));
@@ -6121,7 +6121,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428852e3), SkBits2Float(0xc23d7081));
 path.cubicTo(SkBits2Float(0x42b71f8a), SkBits2Float(0xc15aea65), SkBits2Float(0x42adb77f), SkBits2Float(0x42002593), SkBits2Float(0x42645e8b), SkBits2Float(0x4270faee));
 path.lineTo(SkBits2Float(0x42251616), SkBits2Float(0x422e33d9));
@@ -6136,7 +6136,7 @@
 
 static void battleOp225(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d8749b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4251a993), SkBits2Float(0xc28b9f9f), SkBits2Float(0x4287e789), SkBits2Float(0xc23ea40d));
 path.lineTo(SkBits2Float(0x42447d05), SkBits2Float(0xc209d00a));
@@ -6146,7 +6146,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4287e78a), SkBits2Float(0xc23ea40e));
 path.cubicTo(SkBits2Float(0x428af3dc), SkBits2Float(0xc235f2f3), SkBits2Float(0x428dca5e), SkBits2Float(0xc22cf844), SkBits2Float(0x4290688d), SkBits2Float(0xc223bbef));
 path.lineTo(SkBits2Float(0x4250c881), SkBits2Float(0xc1ecb95a));
@@ -6161,7 +6161,7 @@
 
 static void battleOp226(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d8749b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4251a993), SkBits2Float(0xc28b9f9f), SkBits2Float(0x4287e78a), SkBits2Float(0xc23ea40e));
@@ -6173,7 +6173,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4290688d), SkBits2Float(0xc223bbef));
 path.cubicTo(SkBits2Float(0x42bd187d), SkBits2Float(0xbfc2a74a), SkBits2Float(0x42a250ed), SkBits2Float(0x42421cbf), SkBits2Float(0x42287a28), SkBits2Float(0x428f09b7));
 path.lineTo(SkBits2Float(0x41f394da), SkBits2Float(0x424ecd48));
@@ -6188,7 +6188,7 @@
 
 static void battleOp227(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f1efaa), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42685cb5), SkBits2Float(0xc2851a3e), SkBits2Float(0x429160d2), SkBits2Float(0xc22043b6));
 path.lineTo(SkBits2Float(0x42522f73), SkBits2Float(0xc1e7b52d));
@@ -6198,7 +6198,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429160d2), SkBits2Float(0xc22043b7));
 path.cubicTo(SkBits2Float(0x42943aa0), SkBits2Float(0xc215eba6), SkBits2Float(0x4296cd42), SkBits2Float(0xc20b4794), SkBits2Float(0x429915e6), SkBits2Float(0xc200631e));
 path.lineTo(SkBits2Float(0x425d5418), SkBits2Float(0xc1b99eb9));
@@ -6213,7 +6213,7 @@
 
 static void battleOp228(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f1efa9), SkBits2Float(0xc2a60000), SkBits2Float(0x42685cb5), SkBits2Float(0xc2851a3e), SkBits2Float(0x429160d2), SkBits2Float(0xc22043b7));
@@ -6227,7 +6227,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429915e6), SkBits2Float(0xc200631e));
 path.cubicTo(SkBits2Float(0x42abe101), SkBits2Float(0xc11b0235), SkBits2Float(0x42aa16bb), SkBits2Float(0x417b685c), SkBits2Float(0x42942fff), SkBits2Float(0x42159e77));
 path.cubicTo(SkBits2Float(0x427c9284), SkBits2Float(0x426c62d8), SkBits2Float(0x422cf27d), SkBits2Float(0x4295ccdb), SkBits2Float(0x419d039e), SkBits2Float(0x42a14aca));
@@ -6244,7 +6244,7 @@
 
 static void battleOp229(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4206c976), SkBits2Float(0xc2a60000), SkBits2Float(0x42801937), SkBits2Float(0xc27a823c), SkBits2Float(0x4299a0d7), SkBits2Float(0xc1fb88d1));
 path.lineTo(SkBits2Float(0x425e1cfa), SkBits2Float(0xc1b5d505));
@@ -6254,7 +6254,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4299a0d8), SkBits2Float(0xc1fb88d0));
 path.cubicTo(SkBits2Float(0x429c1b73), SkBits2Float(0xc1e34f53), SkBits2Float(0x429e39d2), SkBits2Float(0xc1ca8528), SkBits2Float(0x429ff920), SkBits2Float(0xc1b14b8c));
 path.lineTo(SkBits2Float(0x42674955), SkBits2Float(0xc1802a45));
@@ -6269,7 +6269,7 @@
 
 static void battleOp230(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4206c976), SkBits2Float(0xc2a60000), SkBits2Float(0x42801937), SkBits2Float(0xc27a823c), SkBits2Float(0x4299a0d8), SkBits2Float(0xc1fb88d0));
@@ -6281,7 +6281,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429ff91f), SkBits2Float(0xc1b14b8a));
 path.cubicTo(SkBits2Float(0x42ae673b), SkBits2Float(0x40783c41), SkBits2Float(0x42a293c2), SkBits2Float(0x41fe6960), SkBits2Float(0x4280464e), SkBits2Float(0x4252ba7b));
 path.cubicTo(SkBits2Float(0x423bf1b3), SkBits2Float(0x42932023), SkBits2Float(0x41a5f32c), SkBits2Float(0x42a99309), SkBits2Float(0xc0c67989), SkBits2Float(0x42a5892f));
@@ -6298,7 +6298,7 @@
 
 static void battleOp231(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421472e7), SkBits2Float(0xc2a5ffff), SkBits2Float(0x428b6da4), SkBits2Float(0xc26973d7), SkBits2Float(0x429fb179), SkBits2Float(0xc1b54986));
 path.lineTo(SkBits2Float(0x4266e1be), SkBits2Float(0xc1830d0f));
@@ -6308,7 +6308,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429fb179), SkBits2Float(0xc1b54988));
 path.cubicTo(SkBits2Float(0x42a1a632), SkBits2Float(0xc199b837), SkBits2Float(0x42a3282f), SkBits2Float(0xc17b594e), SkBits2Float(0x42a43501), SkBits2Float(0xc142a7ba));
 path.lineTo(SkBits2Float(0x426d6865), SkBits2Float(0xc10cb6f0));
@@ -6323,7 +6323,7 @@
 
 static void battleOp232(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x421472e7), SkBits2Float(0xc2a60000), SkBits2Float(0x428b6da4), SkBits2Float(0xc26973d8), SkBits2Float(0x429fb179), SkBits2Float(0xc1b54988));
@@ -6336,7 +6336,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a43502), SkBits2Float(0xc142a7bb));
 path.cubicTo(SkBits2Float(0x42ace9b0), SkBits2Float(0x4189ae79), SkBits2Float(0x429590d6), SkBits2Float(0x423ab1c1), SkBits2Float(0x424df762), SkBits2Float(0x428231a6));
 path.cubicTo(SkBits2Float(0x41e19a31), SkBits2Float(0x42a70a69), SkBits2Float(0xc04a3289), SkBits2Float(0x42b03133), SkBits2Float(0xc1f5f36e), SkBits2Float(0x429a3139));
@@ -6353,7 +6353,7 @@
 
 static void battleOp233(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4220aa02), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42952310), SkBits2Float(0xc258f48d), SkBits2Float(0x42a35f68), SkBits2Float(0xc16b5614));
 path.lineTo(SkBits2Float(0x426c3395), SkBits2Float(0xc12a1f61));
@@ -6363,7 +6363,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a35f69), SkBits2Float(0xc16b5613));
 path.cubicTo(SkBits2Float(0x42a4bd24), SkBits2Float(0xc12ea3c2), SkBits2Float(0x42a59325), SkBits2Float(0xc0e282d6), SkBits2Float(0x42a5dfdf), SkBits2Float(0xc04e84a0));
 path.lineTo(SkBits2Float(0x426fd18d), SkBits2Float(0xc0154a48));
@@ -6378,7 +6378,7 @@
 
 static void battleOp234(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4220aa02), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42952310), SkBits2Float(0xc258f48d), SkBits2Float(0x42a35f69), SkBits2Float(0xc16b5613));
@@ -6391,7 +6391,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5dfdf), SkBits2Float(0xc04e84a0));
 path.cubicTo(SkBits2Float(0x42a85e4f), SkBits2Float(0x41e6959e), SkBits2Float(0x4285b4e3), SkBits2Float(0x426ae44f), SkBits2Float(0x4219b105), SkBits2Float(0x42932450));
 path.cubicTo(SkBits2Float(0x411fe111), SkBits2Float(0x42b0d679), SkBits2Float(0xc1c3966b), SkBits2Float(0x42ab1d42), SkBits2Float(0xc2482755), SkBits2Float(0x428470e8));
@@ -6408,7 +6408,7 @@
 
 static void battleOp235(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422e5e2d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x429f82f2), SkBits2Float(0xc2451c35), SkBits2Float(0x42a59867), SkBits2Float(0xc0b956c5));
 path.lineTo(SkBits2Float(0x426f6a3b), SkBits2Float(0xc085fae3));
@@ -6418,7 +6418,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a59868), SkBits2Float(0xc0b956ca));
 path.cubicTo(SkBits2Float(0x42a62cd8), SkBits2Float(0xbfd2dd07), SkBits2Float(0x42a621be), SkBits2Float(0x4020d557), SkBits2Float(0x42a57734), SkBits2Float(0x40d4ef9c));
 path.lineTo(SkBits2Float(0x426f3a3b), SkBits2Float(0x4099edfc));
@@ -6433,7 +6433,7 @@
 
 static void battleOp236(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x422e5e2d), SkBits2Float(0xc2a5ffff), SkBits2Float(0x429f82f2), SkBits2Float(0xc2451c35), SkBits2Float(0x42a59868), SkBits2Float(0xc0b956ca));
@@ -6446,7 +6446,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a57735), SkBits2Float(0x40d4ef9d));
 path.cubicTo(SkBits2Float(0x429fe5e1), SkBits2Float(0x4225104d), SkBits2Float(0x425fa7d9), SkBits2Float(0x428cf91a), SkBits2Float(0x41b3ea58), SkBits2Float(0x429fca49));
 path.cubicTo(SkBits2Float(0xc12ef606), SkBits2Float(0x42b29b77), SkBits2Float(0xc23abc07), SkBits2Float(0x4299d29d), SkBits2Float(0xc2863a28), SkBits2Float(0x42435615));
@@ -6463,7 +6463,7 @@
 
 static void battleOp237(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41b25a1b), SkBits2Float(0xc2a60000), SkBits2Float(0x422e9a51), SkBits2Float(0xc294100b), SkBits2Float(0x426d0a79), SkBits2Float(0xc26874a1));
 path.cubicTo(SkBits2Float(0x4295bd51), SkBits2Float(0xc228c92e), SkBits2Float(0x42a6d6d5), SkBits2Float(0xc1a5596e), SkBits2Float(0x42a5f7e5), SkBits2Float(0x3fcf7f4c));
@@ -6475,7 +6475,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a5f7e5), SkBits2Float(0x3fcf7f2e));
 path.cubicTo(SkBits2Float(0x42a5cbdf), SkBits2Float(0x40c0b7f8), SkBits2Float(0x42a4eca2), SkBits2Float(0x41268f7d), SkBits2Float(0x42a35c4c), SkBits2Float(0x416be04e));
 path.lineTo(SkBits2Float(0x426c2f14), SkBits2Float(0x412a834e));
@@ -6490,7 +6490,7 @@
 
 static void battleOp238(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41b25a1b), SkBits2Float(0xc2a60000), SkBits2Float(0x422e9a51), SkBits2Float(0xc294100b), SkBits2Float(0x426d0a79), SkBits2Float(0xc26874a1));
@@ -6504,7 +6504,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a35c4c), SkBits2Float(0x416be04e));
 path.cubicTo(SkBits2Float(0x42963d3f), SkBits2Float(0x424c5e0d), SkBits2Float(0x42354f77), SkBits2Float(0x429d76d6), SkBits2Float(0x41096c90), SkBits2Float(0x42a51bdb));
 path.cubicTo(SkBits2Float(0xc1e1325f), SkBits2Float(0x42acc0e0), SkBits2Float(0xc27bf938), SkBits2Float(0x4282ec23), SkBits2Float(0xc299cad8), SkBits2Float(0x41f9ecd8));
@@ -6521,7 +6521,7 @@
 
 static void battleOp239(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ba3f99), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4235f79d), SkBits2Float(0xc29271cf), SkBits2Float(0x4274db3f), SkBits2Float(0xc260354d));
 path.cubicTo(SkBits2Float(0x4299df70), SkBits2Float(0xc21b86fd), SkBits2Float(0x42a97305), SkBits2Float(0xc17e5d7a), SkBits2Float(0x42a55ba0), SkBits2Float(0x40e961b4));
@@ -6533,7 +6533,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a55ba0), SkBits2Float(0x40e961b9));
 path.cubicTo(SkBits2Float(0x42a48d09), SkBits2Float(0x413de0a1), SkBits2Float(0x42a2fc74), SkBits2Float(0x41833376), SkBits2Float(0x42a0adff), SkBits2Float(0x41a6c250));
 path.lineTo(SkBits2Float(0x42684ed9), SkBits2Float(0x417118ef));
@@ -6548,7 +6548,7 @@
 
 static void battleOp240(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ba3f99), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4235f79d), SkBits2Float(0xc29271cf), SkBits2Float(0x4274db3f), SkBits2Float(0xc260354d));
@@ -6562,7 +6562,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a0ae00), SkBits2Float(0x41a6c250));
 path.cubicTo(SkBits2Float(0x428d4422), SkBits2Float(0x4269069e), SkBits2Float(0x42118d33), SkBits2Float(0x42a8086f), SkBits2Float(0xc00fe376), SkBits2Float(0x42a5f066));
 path.cubicTo(SkBits2Float(0xc22389a2), SkBits2Float(0x42a3d85e), SkBits2Float(0xc2935e5d), SkBits2Float(0x42596224), SkBits2Float(0xc2a2b39d), SkBits2Float(0x4183b53a));
@@ -6579,7 +6579,7 @@
 
 static void battleOp241(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c2abe0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423dc4ab), SkBits2Float(0xc290a493), SkBits2Float(0x427cd8fd), SkBits2Float(0xc25727eb));
 path.cubicTo(SkBits2Float(0x429df6a6), SkBits2Float(0xc20d06b1), SkBits2Float(0x42aba628), SkBits2Float(0xc12bcbe5), SkBits2Float(0x42a3dc46), SkBits2Float(0x4154872f));
@@ -6591,7 +6591,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a3dc46), SkBits2Float(0x41548735));
 path.cubicTo(SkBits2Float(0x42a2537f), SkBits2Float(0x41901e3f), SkBits2Float(0x429ff996), SkBits2Float(0x41b55e92), SkBits2Float(0x429cd549), SkBits2Float(0x41d999a0));
 path.lineTo(SkBits2Float(0x4262bf29), SkBits2Float(0x419d4d21));
@@ -6606,7 +6606,7 @@
 
 static void battleOp242(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41c2abe0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x423dc4ab), SkBits2Float(0xc290a493), SkBits2Float(0x427cd8fd), SkBits2Float(0xc25727eb));
@@ -6620,7 +6620,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429cd549), SkBits2Float(0x41d999a0));
 path.cubicTo(SkBits2Float(0x42824b9e), SkBits2Float(0x4282e841), SkBits2Float(0x41d1b597), SkBits2Float(0x42b119ff), SkBits2Float(0xc15b80c3), SkBits2Float(0x42a3b776));
 path.cubicTo(SkBits2Float(0xc2569b2d), SkBits2Float(0x429654ee), SkBits2Float(0xc2a5db0b), SkBits2Float(0x42228c64), SkBits2Float(0xc2a5ffee), SkBits2Float(0x3e172efd));
@@ -6637,7 +6637,7 @@
 
 static void battleOp243(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41caf078), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42455e40), SkBits2Float(0xc28ecc78), SkBits2Float(0x42822b31), SkBits2Float(0xc24e07b4));
 path.cubicTo(SkBits2Float(0x42a1a743), SkBits2Float(0xc1fcecee), SkBits2Float(0x42ad3753), SkBits2Float(0xc0b3be45), SkBits2Float(0x42a18eed), SkBits2Float(0x419892cb));
@@ -6649,7 +6649,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a18eed), SkBits2Float(0x419892ca));
 path.cubicTo(SkBits2Float(0x429f43c9), SkBits2Float(0x41bf6e44), SkBits2Float(0x429c198b), SkBits2Float(0x41e561a5), SkBits2Float(0x42981a0b), SkBits2Float(0x4204fb6e));
 path.lineTo(SkBits2Float(0x425be7f8), SkBits2Float(0x41c0436a));
@@ -6664,7 +6664,7 @@
 
 static void battleOp244(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41caf078), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42455e40), SkBits2Float(0xc28ecc78), SkBits2Float(0x42822b31), SkBits2Float(0xc24e07b4));
@@ -6680,7 +6680,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42981a0b), SkBits2Float(0x4204fb6e));
 path.cubicTo(SkBits2Float(0x426c6b55), SkBits2Float(0x42900555), SkBits2Float(0x417b6a9f), SkBits2Float(0x42b7a6c3), SkBits2Float(0xc1c57072), SkBits2Float(0x429e7dd7));
 path.cubicTo(SkBits2Float(0xc282258c), SkBits2Float(0x428554eb), SkBits2Float(0xc2b314c4), SkBits2Float(0x41cdbc89), SkBits2Float(0xc2a2f571), SkBits2Float(0xc17d09b6));
@@ -6697,7 +6697,7 @@
 
 static void battleOp245(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d28773), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424c4acf), SkBits2Float(0xc28d0a47), SkBits2Float(0x428572fc), SkBits2Float(0xc24574fc));
 path.cubicTo(SkBits2Float(0x42a4c090), SkBits2Float(0xc1e1aad9), SkBits2Float(0x42ae2294), SkBits2Float(0xbf62367e), SkBits2Float(0x429ebce0), SkBits2Float(0x41c23fec));
@@ -6709,7 +6709,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429ebce1), SkBits2Float(0x41c23fee));
 path.cubicTo(SkBits2Float(0x429bb658), SkBits2Float(0x41e9cedc), SkBits2Float(0x4297c4ea), SkBits2Float(0x4208130e), SkBits2Float(0x4292f5c0), SkBits2Float(0x421a62d5));
 path.lineTo(SkBits2Float(0x425478e6), SkBits2Float(0x41df3573));
@@ -6724,7 +6724,7 @@
 
 static void battleOp246(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d28773), SkBits2Float(0xc2a5ffff), SkBits2Float(0x424c4acf), SkBits2Float(0xc28d0a47), SkBits2Float(0x428572fc), SkBits2Float(0xc24574fc));
@@ -6738,7 +6738,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4292f5c1), SkBits2Float(0x421a62d6));
 path.cubicTo(SkBits2Float(0x42541a09), SkBits2Float(0x429b1363), SkBits2Float(0x40b7c75d), SkBits2Float(0x42bb84d6), SkBits2Float(0xc2093cef), SkBits2Float(0x42972755));
 path.cubicTo(SkBits2Float(0xc294b966), SkBits2Float(0x426593a9), SkBits2Float(0xc2ba8c7c), SkBits2Float(0x4131f51c), SkBits2Float(0xc29ad8fe), SkBits2Float(0xc1ef45cd));
@@ -6755,7 +6755,7 @@
 
 static void battleOp247(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d91350), SkBits2Float(0xc2a5ffff), SkBits2Float(0x425238e3), SkBits2Float(0xc28b791f), SkBits2Float(0x428827e4), SkBits2Float(0xc23dec02));
 path.cubicTo(SkBits2Float(0x42a73357), SkBits2Float(0xc1c9cb8b), SkBits2Float(0x42ae86ff), SkBits2Float(0x404daf5b), SkBits2Float(0x429bc6e8), SkBits2Float(0x41e56ae9));
@@ -6767,7 +6767,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429bc6e9), SkBits2Float(0x41e56aeb));
 path.cubicTo(SkBits2Float(0x429818bd), SkBits2Float(0x4206b36a), SkBits2Float(0x42937671), SkBits2Float(0x4219f01e), SkBits2Float(0x428df070), SkBits2Float(0x422c2771));
 path.lineTo(SkBits2Float(0x424d369d), SkBits2Float(0x41f8e5bf));
@@ -6782,7 +6782,7 @@
 
 static void battleOp248(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41d91350), SkBits2Float(0xc2a5ffff), SkBits2Float(0x425238e3), SkBits2Float(0xc28b791f), SkBits2Float(0x428827e4), SkBits2Float(0xc23dec02));
@@ -6797,7 +6797,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428df071), SkBits2Float(0x422c2771));
 path.cubicTo(SkBits2Float(0x423d9ebb), SkBits2Float(0x42a3ca6a), SkBits2Float(0xc041a78f), SkBits2Float(0x42bd279e), SkBits2Float(0xc228abe7), SkBits2Float(0x428efaad));
 path.cubicTo(SkBits2Float(0xc2a29eac), SkBits2Float(0x42419b78), SkBits2Float(0xc2bd3710), SkBits2Float(0xbfef63d4), SkBits2Float(0xc2900003), SkBits2Float(0xc2252a98));
@@ -6814,7 +6814,7 @@
 
 static void battleOp249(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41df6bc7), SkBits2Float(0xc2a60000), SkBits2Float(0x4257ee8b), SkBits2Float(0xc289e8f6), SkBits2Float(0x428aab73), SkBits2Float(0xc2368066));
 path.cubicTo(SkBits2Float(0x42a95fa1), SkBits2Float(0xc1b25dc1), SkBits2Float(0x42ae8dc1), SkBits2Float(0x40e61789), SkBits2Float(0x42987459), SkBits2Float(0x42035b41));
@@ -6826,7 +6826,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42987459), SkBits2Float(0x42035b41));
 path.cubicTo(SkBits2Float(0x42941f1a), SkBits2Float(0x421778e1), SkBits2Float(0x428ecdc9), SkBits2Float(0x422aae55), SkBits2Float(0x42889449), SkBits2Float(0x423cb3b9));
 path.lineTo(SkBits2Float(0x424576c5), SkBits2Float(0x4208693e));
@@ -6841,7 +6841,7 @@
 
 static void battleOp250(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb69400ae), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41df6bc7), SkBits2Float(0xc2a60000), SkBits2Float(0x4257ee8b), SkBits2Float(0xc289e8f6), SkBits2Float(0x428aab73), SkBits2Float(0xc2368066));
@@ -6855,7 +6855,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42889449), SkBits2Float(0x423cb3b8));
 path.cubicTo(SkBits2Float(0x424c5291), SkBits2Float(0x42902c61), SkBits2Float(0x41ad609d), SkBits2Float(0x42ab4d26), SkBits2Float(0xc1072a9c), SkBits2Float(0x42a52356));
 path.cubicTo(SkBits2Float(0xc21a459c), SkBits2Float(0x429ef985), SkBits2Float(0xc2813d9b), SkBits2Float(0x4270fef6), SkBits2Float(0xc298db30), SkBits2Float(0x420179e4));
@@ -6874,7 +6874,7 @@
 
 static void battleOp251(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e529f0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x425d10b2), SkBits2Float(0xc2887541), SkBits2Float(0x428cd9cf), SkBits2Float(0xc22fb184));
 path.cubicTo(SkBits2Float(0x42ab2b45), SkBits2Float(0xc19cf10c), SkBits2Float(0x42ae472d), SkBits2Float(0x412c96c0), SkBits2Float(0x42951360), SkBits2Float(0x42120c0d));
@@ -6886,7 +6886,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42951360), SkBits2Float(0x42120c0f));
 path.cubicTo(SkBits2Float(0x429023a5), SkBits2Float(0x422633cd), SkBits2Float(0x428a3193), SkBits2Float(0x42394df4), SkBits2Float(0x42835484), SkBits2Float(0x424b0f7e));
 path.lineTo(SkBits2Float(0x423ddffa), SkBits2Float(0x4212ca6e));
@@ -6901,7 +6901,7 @@
 
 static void battleOp252(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3725ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41e529f0), SkBits2Float(0xc2a5ffff), SkBits2Float(0x425d10b2), SkBits2Float(0xc2887541), SkBits2Float(0x428cd9cf), SkBits2Float(0xc22fb184));
@@ -6915,7 +6915,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42835484), SkBits2Float(0x424b0f7e));
 path.cubicTo(SkBits2Float(0x423aab34), SkBits2Float(0x4296ad9b), SkBits2Float(0x41789cf4), SkBits2Float(0x42ae7f70), SkBits2Float(0xc1702bd2), SkBits2Float(0x42a3434e));
 path.cubicTo(SkBits2Float(0xc2363d27), SkBits2Float(0x4298072c), SkBits2Float(0xc28cd4c4), SkBits2Float(0x42573cf7), SkBits2Float(0xc29edb8e), SkBits2Float(0x41c0adb0));
@@ -6934,7 +6934,7 @@
 
 static void battleOp253(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ea9e19), SkBits2Float(0xc2a60000), SkBits2Float(0x4261e8db), SkBits2Float(0xc2870be6), SkBits2Float(0x428ed6bc), SkBits2Float(0xc22926d7));
 path.cubicTo(SkBits2Float(0x42acb90a), SkBits2Float(0xc1886bc1), SkBits2Float(0x42adc0f7), SkBits2Float(0x41631db6), SkBits2Float(0x42918cff), SkBits2Float(0x421fa302));
@@ -6946,7 +6946,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42918d00), SkBits2Float(0x421fa301));
 path.cubicTo(SkBits2Float(0x428c0830), SkBits2Float(0x4233c399), SkBits2Float(0x42857bfe), SkBits2Float(0x4246b13f), SkBits2Float(0x427c06a0), SkBits2Float(0x42581e30));
 path.lineTo(SkBits2Float(0x42362ff8), SkBits2Float(0x421c3ad6));
@@ -6961,7 +6961,7 @@
 
 static void battleOp254(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41ea9e19), SkBits2Float(0xc2a60000), SkBits2Float(0x4261e8db), SkBits2Float(0xc2870be6), SkBits2Float(0x428ed6bc), SkBits2Float(0xc22926d7));
@@ -6975,7 +6975,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427c069f), SkBits2Float(0x42581e31));
 path.cubicTo(SkBits2Float(0x4229355f), SkBits2Float(0x429c5901), SkBits2Float(0x4119ef9b), SkBits2Float(0x42b0b9f6), SkBits2Float(0xc1a91754), SkBits2Float(0x42a086fc));
 path.cubicTo(SkBits2Float(0xc24f933a), SkBits2Float(0x42905402), SkBits2Float(0xc296a2af), SkBits2Float(0x423cccf9), SkBits2Float(0xc2a2e3f0), SkBits2Float(0x417fd713));
@@ -6994,7 +6994,7 @@
 
 static void battleOp255(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41eeb164), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42658277), SkBits2Float(0xc285f892), SkBits2Float(0x42904565), SkBits2Float(0xc22437b5));
 path.cubicTo(SkBits2Float(0x42adc98d), SkBits2Float(0xc171f916), SkBits2Float(0x42ad3226), SkBits2Float(0x4185deb6), SkBits2Float(0x428eb8d5), SkBits2Float(0x42298bae));
@@ -7006,7 +7006,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428eb8d5), SkBits2Float(0x42298bad));
 path.cubicTo(SkBits2Float(0x4288c365), SkBits2Float(0x423d9c15), SkBits2Float(0x4281c36f), SkBits2Float(0x42505c7e), SkBits2Float(0x4273ad50), SkBits2Float(0x42617d52));
 path.lineTo(SkBits2Float(0x423026ec), SkBits2Float(0x42230126));
@@ -7021,7 +7021,7 @@
 
 static void battleOp256(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb69400ae), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41eeb164), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42658277), SkBits2Float(0xc285f892), SkBits2Float(0x42904565), SkBits2Float(0xc22437b5));
@@ -7036,7 +7036,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4273ad4f), SkBits2Float(0x42617d52));
 path.cubicTo(SkBits2Float(0x421bc173), SkBits2Float(0x42a0404f), SkBits2Float(0x40a50405), SkBits2Float(0x42b1dfaa), SkBits2Float(0xc1cd0022), SkBits2Float(0x429de3fd));
 path.cubicTo(SkBits2Float(0xc261a0a2), SkBits2Float(0x4289e850), SkBits2Float(0xc29d25ee), SkBits2Float(0x4227ed4e), SkBits2Float(0xc2a4d3d8), SkBits2Float(0x411d8f80));
@@ -7055,7 +7055,7 @@
 
 static void battleOp257(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f2d268), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426923a2), SkBits2Float(0xc284dd06), SkBits2Float(0x4291aced), SkBits2Float(0xc21f2e53));
 path.cubicTo(SkBits2Float(0x42aec809), SkBits2Float(0xc1528a66), SkBits2Float(0x42ac7c90), SkBits2Float(0x419a60b1), SkBits2Float(0x428bb0fe), SkBits2Float(0x42335ba0));
@@ -7067,7 +7067,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428bb0ff), SkBits2Float(0x42335ba2));
 path.cubicTo(SkBits2Float(0x4285489d), SkBits2Float(0x42475206), SkBits2Float(0x427ba631), SkBits2Float(0x4259da14), SkBits2Float(0x426ae250), SkBits2Float(0x426aa282));
 path.lineTo(SkBits2Float(0x4229cbb3), SkBits2Float(0x42299d92));
@@ -7082,7 +7082,7 @@
 
 static void battleOp258(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36d3ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f2d268), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426923a2), SkBits2Float(0xc284dd06), SkBits2Float(0x4291aced), SkBits2Float(0xc21f2e53));
@@ -7096,7 +7096,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x426ae251), SkBits2Float(0x426aa281));
 path.cubicTo(SkBits2Float(0x420dcd2c), SkBits2Float(0x42a3e87c), SkBits2Float(0x3f1c0197), SkBits2Float(0x42b294d6), SkBits2Float(0xc1f0a2ab), SkBits2Float(0x429ab731));
 path.cubicTo(SkBits2Float(0xc27312b1), SkBits2Float(0x4282d98e), SkBits2Float(0xc2a300b1), SkBits2Float(0x4211eaa7), SkBits2Float(0xc2a5d865), SkBits2Float(0x40654aaf));
@@ -7115,7 +7115,7 @@
 
 static void battleOp259(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f70d18), SkBits2Float(0xc2a60000), SkBits2Float(0x426cd682), SkBits2Float(0xc283b5d2), SkBits2Float(0x429310ae), SkBits2Float(0xc219fc22));
 path.cubicTo(SkBits2Float(0x42afb61c), SkBits2Float(0xc132327f), SkBits2Float(0x42ab9c4e), SkBits2Float(0x41af4ab2), SkBits2Float(0x42886baa), SkBits2Float(0x423d2918));
@@ -7127,7 +7127,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42886bab), SkBits2Float(0x423d2917));
 path.cubicTo(SkBits2Float(0x42818ce6), SkBits2Float(0x4250fab6), SkBits2Float(0x42733ded), SkBits2Float(0x42633df9), SkBits2Float(0x42618b96), SkBits2Float(0x4273a01b));
 path.lineTo(SkBits2Float(0x42230b75), SkBits2Float(0x42301d61));
@@ -7142,7 +7142,7 @@
 
 static void battleOp260(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f70d18), SkBits2Float(0xc2a60000), SkBits2Float(0x426cd682), SkBits2Float(0xc283b5d2), SkBits2Float(0x429310ae), SkBits2Float(0xc219fc22));
@@ -7156,7 +7156,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42618b95), SkBits2Float(0x4273a01c));
 path.cubicTo(SkBits2Float(0x41fe659e), SkBits2Float(0x42a75638), SkBits2Float(0xc081f8cf), SkBits2Float(0x42b2d4b3), SkBits2Float(0xc20a1eaa), SkBits2Float(0x4296f3e7));
 path.cubicTo(SkBits2Float(0xc281ff1e), SkBits2Float(0x42762634), SkBits2Float(0xc2a8320c), SkBits2Float(0x41f52b39), SkBits2Float(0xc2a5e71e), SkBits2Float(0xc035be80));
@@ -7175,7 +7175,7 @@
 
 static void battleOp261(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f9750b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426eeefa), SkBits2Float(0xc2830bb8), SkBits2Float(0x4293d569), SkBits2Float(0xc2170343));
 path.cubicTo(SkBits2Float(0x42b03354), SkBits2Float(0xc11fbc55), SkBits2Float(0x42ab0b89), SkBits2Float(0x41bb247a), SkBits2Float(0x42867c8e), SkBits2Float(0x42429f12));
@@ -7187,7 +7187,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42867c8e), SkBits2Float(0x42429f13));
 path.cubicTo(SkBits2Float(0x427eb473), SkBits2Float(0x4256572c), SkBits2Float(0x426e4fbb), SkBits2Float(0x42686e49), SkBits2Float(0x425c16a2), SkBits2Float(0x427890ea));
 path.lineTo(SkBits2Float(0x421f199c), SkBits2Float(0x4233afb3));
@@ -7202,7 +7202,7 @@
 
 static void battleOp262(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3725ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41f9750b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x426eeefa), SkBits2Float(0xc2830bb8), SkBits2Float(0x4293d569), SkBits2Float(0xc2170343));
@@ -7216,7 +7216,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x425c16a1), SkBits2Float(0x427890eb));
 path.cubicTo(SkBits2Float(0x41ed85e5), SkBits2Float(0x42a9245e), SkBits2Float(0xc0d70d9a), SkBits2Float(0x42b2c211), SkBits2Float(0xc2140612), SkBits2Float(0x42949665));
 path.cubicTo(SkBits2Float(0xc2869539), SkBits2Float(0x426cd56f), SkBits2Float(0xc2aac701), SkBits2Float(0x41d9ff9c), SkBits2Float(0xc2a57e3b), SkBits2Float(0xc0cf6824));
@@ -7235,7 +7235,7 @@
 
 static void battleOp263(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fc38da), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4271556b), SkBits2Float(0xc2824656), SkBits2Float(0x4294b266), SkBits2Float(0xc213956f));
 path.cubicTo(SkBits2Float(0x42b0ba15), SkBits2Float(0xc10a78c9), SkBits2Float(0x42aa55de), SkBits2Float(0x41c8b65d), SkBits2Float(0x42843343), SkBits2Float(0x4248ca15));
@@ -7247,7 +7247,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42843344), SkBits2Float(0x4248ca14));
 path.cubicTo(SkBits2Float(0x4279865a), SkBits2Float(0x425c60b2), SkBits2Float(0x426884b7), SkBits2Float(0x426e4097), SkBits2Float(0x4255b1c1), SkBits2Float(0x427e1584));
 path.lineTo(SkBits2Float(0x421a7a55), SkBits2Float(0x4237acdc));
@@ -7262,7 +7262,7 @@
 
 static void battleOp264(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fc38da), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4271556b), SkBits2Float(0xc2824656), SkBits2Float(0x4294b266), SkBits2Float(0xc213956f));
@@ -7276,7 +7276,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4255b1c2), SkBits2Float(0x427e1586));
 path.cubicTo(SkBits2Float(0x41d9eb88), SkBits2Float(0x42ab15b8), SkBits2Float(0xc11c5ee2), SkBits2Float(0x42b27b8c), SkBits2Float(0xc21f2fec), SkBits2Float(0x4291ac82));
 path.cubicTo(SkBits2Float(0xc28ba40f), SkBits2Float(0x4261baf0), SkBits2Float(0xc2ad6782), SkBits2Float(0x41ba4aab), SkBits2Float(0xc2a4a120), SkBits2Float(0xc12a4d95));
@@ -7295,7 +7295,7 @@
 
 static void battleOp265(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fe7454), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427343e8), SkBits2Float(0xc281a57b), SkBits2Float(0x429560d9), SkBits2Float(0xc210ce12));
 path.cubicTo(SkBits2Float(0x42b11fbd), SkBits2Float(0xc0f2896e), SkBits2Float(0x42a9b750), SkBits2Float(0x41d3a0ba), SkBits2Float(0x42824e39), SkBits2Float(0x424daf12));
@@ -7307,7 +7307,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42824e38), SkBits2Float(0x424daf15));
 path.cubicTo(SkBits2Float(0x42753e9a), SkBits2Float(0x4261276c), SkBits2Float(0x4263be9a), SkBits2Float(0x4272d73c), SkBits2Float(0x4250704b), SkBits2Float(0x428134df));
 path.lineTo(SkBits2Float(0x4216adb6), SkBits2Float(0x423acdfc));
@@ -7322,7 +7322,7 @@
 
 static void battleOp266(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x41fe7454), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427343e8), SkBits2Float(0xc281a57b), SkBits2Float(0x429560d9), SkBits2Float(0xc210ce12));
@@ -7338,7 +7338,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4250704d), SkBits2Float(0x428134e0));
 path.cubicTo(SkBits2Float(0x41c9effb), SkBits2Float(0x42ac8cba), SkBits2Float(0xc143bd6b), SkBits2Float(0x42b21c58), SkBits2Float(0xc2280561), SkBits2Float(0x428f2c0c));
 path.cubicTo(SkBits2Float(0xc28f8db2), SkBits2Float(0x42587782), SkBits2Float(0xc2af41ba), SkBits2Float(0x41a05b8a), SkBits2Float(0xc2a3a0d2), SkBits2Float(0xc15fb01a));
@@ -7357,7 +7357,7 @@
 
 static void battleOp267(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42003b3a), SkBits2Float(0xc2a60000), SkBits2Float(0x4274ff8d), SkBits2Float(0xc28113a0), SkBits2Float(0x4295fac2), SkBits2Float(0xc20e4c24));
 path.cubicTo(SkBits2Float(0x42b175be), SkBits2Float(0xc0d38840), SkBits2Float(0x42a91fa3), SkBits2Float(0x41dd6a3d), SkBits2Float(0x42809081), SkBits2Float(0x4252054f));
@@ -7369,7 +7369,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42809082), SkBits2Float(0x4252054e));
 path.cubicTo(SkBits2Float(0x4271521d), SkBits2Float(0x42655feb), SkBits2Float(0x425f60c7), SkBits2Float(0x4276e1ca), SkBits2Float(0x424ba43f), SkBits2Float(0x42831ae1));
 path.lineTo(SkBits2Float(0x421335f7), SkBits2Float(0x423d8ca7));
@@ -7384,7 +7384,7 @@
 
 static void battleOp268(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3697ff52), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42003b3a), SkBits2Float(0xc2a60000), SkBits2Float(0x4274ff8d), SkBits2Float(0xc28113a0), SkBits2Float(0x4295fac2), SkBits2Float(0xc20e4c24));
@@ -7398,7 +7398,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x424ba440), SkBits2Float(0x42831ae2));
 path.cubicTo(SkBits2Float(0x41bb72ba), SkBits2Float(0x42adc9b8), SkBits2Float(0xc16714ca), SkBits2Float(0x42b1a998), SkBits2Float(0xc22fd30d), SkBits2Float(0x428ccf5c));
 path.cubicTo(SkBits2Float(0xc292f074), SkBits2Float(0x424fea41), SkBits2Float(0xc2b0b757), SkBits2Float(0x4188cdbd), SkBits2Float(0xc2a27f7d), SkBits2Float(0xc187abb1));
@@ -7417,7 +7417,7 @@
 
 static void battleOp269(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42011047), SkBits2Float(0xc2a60000), SkBits2Float(0x42766e56), SkBits2Float(0xc28099ef), SkBits2Float(0x42967824), SkBits2Float(0xc20c36c8));
 path.cubicTo(SkBits2Float(0x42b1b91c), SkBits2Float(0xc0b9cd9b), SkBits2Float(0x42a89b7a), SkBits2Float(0x41e5804f), SkBits2Float(0x427e310b), SkBits2Float(0x42559106));
@@ -7429,7 +7429,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427e3109), SkBits2Float(0x42559108));
 path.cubicTo(SkBits2Float(0x426e0477), SkBits2Float(0x4268d13b), SkBits2Float(0x425bb575), SkBits2Float(0x427a2b1d), SkBits2Float(0x42479e2a), SkBits2Float(0x4284a4a0));
 path.lineTo(SkBits2Float(0x42104d52), SkBits2Float(0x423fc5ea));
@@ -7444,7 +7444,7 @@
 
 static void battleOp270(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7060057), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42011047), SkBits2Float(0xc2a60000), SkBits2Float(0x42766e56), SkBits2Float(0xc28099ef), SkBits2Float(0x42967824), SkBits2Float(0xc20c36c8));
@@ -7453,7 +7453,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42479e29), SkBits2Float(0x4284a4a0));
 path.cubicTo(SkBits2Float(0x41af5d68), SkBits2Float(0x42aec1b4), SkBits2Float(0xc1822698), SkBits2Float(0x42b135a9), SkBits2Float(0xc2362f3e), SkBits2Float(0x428ac623));
 path.cubicTo(SkBits2Float(0xc295a599), SkBits2Float(0x4248ad36), SkBits2Float(0xc2b1c6ab), SkBits2Float(0x416a48a9), SkBits2Float(0xc2a165f3), SkBits2Float(0xc19b42cf));
@@ -7472,7 +7472,7 @@
 
 static void battleOp271(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4201b43a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4277880a), SkBits2Float(0xc2803bc7), SkBits2Float(0x4296d747), SkBits2Float(0xc20a9b85));
 path.cubicTo(SkBits2Float(0x42b1ea89), SkBits2Float(0xc0a5fbe3), SkBits2Float(0x42a831cc), SkBits2Float(0x41ebb52f), SkBits2Float(0x427be65b), SkBits2Float(0x425843c9));
@@ -7484,7 +7484,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x427be65e), SkBits2Float(0x425843c9));
 path.cubicTo(SkBits2Float(0x426b71bd), SkBits2Float(0x426b6e8c), SkBits2Float(0x4258dad9), SkBits2Float(0x427ca87a), SkBits2Float(0x42447e14), SkBits2Float(0x4285cdfb));
 path.lineTo(SkBits2Float(0x420e0af4), SkBits2Float(0x424173d3));
@@ -7499,7 +7499,7 @@
 
 static void battleOp272(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4201b43a), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4277880a), SkBits2Float(0xc2803bc7), SkBits2Float(0x4296d747), SkBits2Float(0xc20a9b85));
@@ -7515,7 +7515,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42447e16), SkBits2Float(0x4285cdfb));
 path.cubicTo(SkBits2Float(0x41a605d7), SkBits2Float(0x42af776a), SkBits2Float(0xc18d5e26), SkBits2Float(0x42b0cfa2), SkBits2Float(0xc23b02ad), SkBits2Float(0x428928e1));
 path.cubicTo(SkBits2Float(0xc297ab24), SkBits2Float(0x42430442), SkBits2Float(0xc2b27fa9), SkBits2Float(0x414bdf0d), SkBits2Float(0xc2a073c8), SkBits2Float(0xc1aa3a13));
@@ -7534,7 +7534,7 @@
 
 static void battleOp273(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42023f77), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427876e4), SkBits2Float(0xc27fd6f4), SkBits2Float(0x42972728), SkBits2Float(0xc2093dbb));
 path.cubicTo(SkBits2Float(0x42b212de), SkBits2Float(0xc0952410), SkBits2Float(0x42a7d55b), SkBits2Float(0x41f0f791), SkBits2Float(0x4279eebf), SkBits2Float(0x425a890b));
@@ -7546,7 +7546,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4279eebd), SkBits2Float(0x425a890e));
 path.cubicTo(SkBits2Float(0x42693cf3), SkBits2Float(0x426da0dc), SkBits2Float(0x42566929), SkBits2Float(0x427ebed8), SkBits2Float(0x4241d1ac), SkBits2Float(0x4286c6a2));
 path.lineTo(SkBits2Float(0x420c1c33), SkBits2Float(0x4242db53));
@@ -7561,7 +7561,7 @@
 
 static void battleOp274(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015d), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42023f77), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427876e4), SkBits2Float(0xc27fd6f4), SkBits2Float(0x42972728), SkBits2Float(0xc2093dbb));
@@ -7573,7 +7573,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4241d1ad), SkBits2Float(0x4286c6a2));
 path.cubicTo(SkBits2Float(0x419e0f8e), SkBits2Float(0x42b00b7b), SkBits2Float(0xc196dfc4), SkBits2Float(0x42b07042), SkBits2Float(0xc23f0fa7), SkBits2Float(0x4287c1be));
 path.cubicTo(SkBits2Float(0xc29957b6), SkBits2Float(0x423e2672), SkBits2Float(0xc2b30c7a), SkBits2Float(0x4131f351), SkBits2Float(0xc29f94d8), SkBits2Float(0xc1b6db1d));
@@ -7592,7 +7592,7 @@
 
 static void battleOp275(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202aab9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42792ea4), SkBits2Float(0xc27f5acc), SkBits2Float(0x4297641b), SkBits2Float(0xc2082fee));
 path.cubicTo(SkBits2Float(0x42b230e5), SkBits2Float(0xc0882884), SkBits2Float(0x42a78c73), SkBits2Float(0x41f502e3), SkBits2Float(0x4278676f), SkBits2Float(0x425c4571));
@@ -7604,7 +7604,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42786771), SkBits2Float(0x425c4570));
 path.cubicTo(SkBits2Float(0x42678692), SkBits2Float(0x426f4e2b), SkBits2Float(0x425483f6), SkBits2Float(0x42802b0f), SkBits2Float(0x423fbf6b), SkBits2Float(0x428783bc));
 path.lineTo(SkBits2Float(0x420a9ce1), SkBits2Float(0x4243ecb9));
@@ -7619,7 +7619,7 @@
 
 static void battleOp276(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3725ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202aab9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x42792ea4), SkBits2Float(0xc27f5acc), SkBits2Float(0x4297641b), SkBits2Float(0xc2082fee));
@@ -7634,7 +7634,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423fbf6b), SkBits2Float(0x428783bc));
 path.cubicTo(SkBits2Float(0x4197e908), SkBits2Float(0x42b0799e), SkBits2Float(0xc19e2f01), SkBits2Float(0x42b0215b), SkBits2Float(0xc24226b0), SkBits2Float(0x4286a80b));
 path.cubicTo(SkBits2Float(0xc29a9aef), SkBits2Float(0x423a5d79), SkBits2Float(0xc2b36ebb), SkBits2Float(0x411dee4a), SkBits2Float(0xc29ede64), SkBits2Float(0xc1c087c1));
@@ -7653,7 +7653,7 @@
 
 static void battleOp277(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202f62b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4279afc7), SkBits2Float(0xc27f0340), SkBits2Float(0x42978eaf), SkBits2Float(0xc20771fd));
 path.cubicTo(SkBits2Float(0x42b2457b), SkBits2Float(0xc07e0b91), SkBits2Float(0x42a7584a), SkBits2Float(0x41f7da1e), SkBits2Float(0x42775276), SkBits2Float(0x425d7c3f));
@@ -7665,7 +7665,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42775277), SkBits2Float(0x425d7c41));
 path.cubicTo(SkBits2Float(0x4266507b), SkBits2Float(0x42707a20), SkBits2Float(0x42532cff), SkBits2Float(0x4280b928), SkBits2Float(0x423e48db), SkBits2Float(0x42880779));
 path.lineTo(SkBits2Float(0x42098e1c), SkBits2Float(0x4244ab32));
@@ -7680,7 +7680,7 @@
 
 static void battleOp278(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7240057), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x4202f62b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x4279afc7), SkBits2Float(0xc27f0340), SkBits2Float(0x42978eaf), SkBits2Float(0xc20771fd));
@@ -7695,7 +7695,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423e48db), SkBits2Float(0x4288077a));
 path.cubicTo(SkBits2Float(0x41939344), SkBits2Float(0x42b0c509), SkBits2Float(0xc1a3515b), SkBits2Float(0x42afe6ff), SkBits2Float(0xc2444efb), SkBits2Float(0x4285df44));
 path.cubicTo(SkBits2Float(0xc29b7aa2), SkBits2Float(0x4237af14), SkBits2Float(0xc2b3ae7d), SkBits2Float(0x410fd2d1), SkBits2Float(0xc29e5879), SkBits2Float(0xc1c74e5b));
@@ -7714,7 +7714,7 @@
 
 static void battleOp279(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420331e6), SkBits2Float(0xc2a60000), SkBits2Float(0x427a15f4), SkBits2Float(0xc27ebdd3), SkBits2Float(0x4297b03a), SkBits2Float(0xc206db86));
 path.cubicTo(SkBits2Float(0x42b2557a), SkBits2Float(0xc06f9378), SkBits2Float(0x42a72e7e), SkBits2Float(0x41fa194a), SkBits2Float(0x4276762d), SkBits2Float(0x425e7148));
@@ -7726,7 +7726,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4276762e), SkBits2Float(0x425e7147));
 path.cubicTo(SkBits2Float(0x42655a01), SkBits2Float(0x42716669), SkBits2Float(0x42521c84), SkBits2Float(0x428128fd), SkBits2Float(0x423d1f69), SkBits2Float(0x42886f05));
 path.lineTo(SkBits2Float(0x4208b718), SkBits2Float(0x424540e7));
@@ -7741,7 +7741,7 @@
 
 static void battleOp280(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3707ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420331e6), SkBits2Float(0xc2a60000), SkBits2Float(0x427a15f4), SkBits2Float(0xc27ebdd3), SkBits2Float(0x4297b03a), SkBits2Float(0xc206db86));
@@ -7757,7 +7757,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423d1f69), SkBits2Float(0x42886f06));
 path.cubicTo(SkBits2Float(0x4190236c), SkBits2Float(0x42b0ff8c), SkBits2Float(0xc1a760b7), SkBits2Float(0x42afb726), SkBits2Float(0xc24601c7), SkBits2Float(0x42853ece));
 path.cubicTo(SkBits2Float(0xc29c2998), SkBits2Float(0x42358ced), SkBits2Float(0xc2b3ddd5), SkBits2Float(0x4104a433), SkBits2Float(0xc29deb35), SkBits2Float(0xc1cca70e));
@@ -7776,7 +7776,7 @@
 
 static void battleOp281(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42035955), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427a595d), SkBits2Float(0xc27e8fe6), SkBits2Float(0x4297c647), SkBits2Float(0xc206781b));
 path.cubicTo(SkBits2Float(0x42b25fdf), SkBits2Float(0xc0660504), SkBits2Float(0x42a712a2), SkBits2Float(0x41fb94c7), SkBits2Float(0x4275e43b), SkBits2Float(0x425f1290));
@@ -7788,7 +7788,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4275e43b), SkBits2Float(0x425f1292));
 path.cubicTo(SkBits2Float(0x4264b6c3), SkBits2Float(0x427201df), SkBits2Float(0x4251681e), SkBits2Float(0x42817283), SkBits2Float(0x423c5a8f), SkBits2Float(0x4288b309));
 path.lineTo(SkBits2Float(0x420828ca), SkBits2Float(0x4245a33c));
@@ -7803,7 +7803,7 @@
 
 static void battleOp282(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3637fea5), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42035955), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427a595d), SkBits2Float(0xc27e8fe6), SkBits2Float(0x4297c647), SkBits2Float(0xc206781b));
@@ -7818,7 +7818,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423c5a8f), SkBits2Float(0x4288b30a));
 path.cubicTo(SkBits2Float(0x418dddd4), SkBits2Float(0x42b12599), SkBits2Float(0xc1aa0e7c), SkBits2Float(0x42af96c0), SkBits2Float(0xc2471fb7), SkBits2Float(0x4284d41e));
 path.cubicTo(SkBits2Float(0xc29c9c18), SkBits2Float(0x423422f8), SkBits2Float(0xc2b3fb95), SkBits2Float(0x40fa8096), SkBits2Float(0xc29da17e), SkBits2Float(0xc1d02ca0));
@@ -7837,7 +7837,7 @@
 
 static void battleOp283(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42036bf7), SkBits2Float(0xc2a60000), SkBits2Float(0x427a7934), SkBits2Float(0xc27e7a35), SkBits2Float(0x4297d0ad), SkBits2Float(0xc2064926));
 path.cubicTo(SkBits2Float(0x42b264c0), SkBits2Float(0xc061818a), SkBits2Float(0x42a70569), SkBits2Float(0x41fc47ee), SkBits2Float(0x42759f2d), SkBits2Float(0x425f5e99));
@@ -7849,7 +7849,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42759f2b), SkBits2Float(0x425f5e9b));
 path.cubicTo(SkBits2Float(0x42646988), SkBits2Float(0x42724b20), SkBits2Float(0x425112cb), SkBits2Float(0x42819524), SkBits2Float(0x423bfd7a), SkBits2Float(0x4288d30e));
 path.lineTo(SkBits2Float(0x4207e580), SkBits2Float(0x4245d187));
@@ -7864,7 +7864,7 @@
 
 static void battleOp284(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3707ffa9), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42036bf7), SkBits2Float(0xc2a60000), SkBits2Float(0x427a7934), SkBits2Float(0xc27e7a35), SkBits2Float(0x4297d0ad), SkBits2Float(0xc2064926));
@@ -7873,7 +7873,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bfd7a), SkBits2Float(0x4288d30e));
 path.cubicTo(SkBits2Float(0x418ccafd), SkBits2Float(0x42b13768), SkBits2Float(0xc1ab522b), SkBits2Float(0x42af873b), SkBits2Float(0xc247a66c), SkBits2Float(0x4284a188));
 path.cubicTo(SkBits2Float(0xc29cd1e0), SkBits2Float(0x423377ac), SkBits2Float(0xc2b40936), SkBits2Float(0x40f384e7), SkBits2Float(0xc29d7e41), SkBits2Float(0xc1d1d5b9));
@@ -7892,7 +7892,7 @@
 
 static void battleOp285(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420374f9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427a8897), SkBits2Float(0xc27e6fb3), SkBits2Float(0x4297d5b1), SkBits2Float(0xc2063270));
 path.cubicTo(SkBits2Float(0x42b26718), SkBits2Float(0xc05f52ba), SkBits2Float(0x42a6ff00), SkBits2Float(0x41fc9e87), SkBits2Float(0x42757dbf), SkBits2Float(0x425f8353));
@@ -7904,7 +7904,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757dc1), SkBits2Float(0x425f8353));
 path.cubicTo(SkBits2Float(0x4264442b), SkBits2Float(0x42726e80), SkBits2Float(0x4250e985), SkBits2Float(0x4281a5dc), SkBits2Float(0x423bd072), SkBits2Float(0x4288e283));
 path.lineTo(SkBits2Float(0x4207c4f4), SkBits2Float(0x4245e7df));
@@ -7919,7 +7919,7 @@
 
 static void battleOp286(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb630015b), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420374f9), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427a8897), SkBits2Float(0xc27e6fb3), SkBits2Float(0x4297d5b1), SkBits2Float(0xc2063270));
@@ -7933,7 +7933,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bd073), SkBits2Float(0x4288e283));
 path.cubicTo(SkBits2Float(0x418c461b), SkBits2Float(0x42b13ffc), SkBits2Float(0xc1abee9c), SkBits2Float(0x42af7fac), SkBits2Float(0xc247e775), SkBits2Float(0x42848907));
 path.cubicTo(SkBits2Float(0xc29cebcd), SkBits2Float(0x423324c4), SkBits2Float(0xc2b40fb2), SkBits2Float(0x40f02474), SkBits2Float(0xc29d6d1c), SkBits2Float(0xc1d2a316));
@@ -7952,7 +7952,7 @@
 
 static void battleOp287(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420377c9), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8d67), SkBits2Float(0xc27e6c6d), SkBits2Float(0x4297d744), SkBits2Float(0xc2062b59));
 path.cubicTo(SkBits2Float(0x42b267d3), SkBits2Float(0xc05ea43d), SkBits2Float(0x42a6fd01), SkBits2Float(0x41fcb991), SkBits2Float(0x42757351), SkBits2Float(0x425f8ecb));
@@ -7964,7 +7964,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757350), SkBits2Float(0x425f8ecb));
 path.cubicTo(SkBits2Float(0x42643881), SkBits2Float(0x4272798e), SkBits2Float(0x4250dca0), SkBits2Float(0x4281ab15), SkBits2Float(0x423bc262), SkBits2Float(0x4288e756));
 path.lineTo(SkBits2Float(0x4207bac8), SkBits2Float(0x4245eed9));
@@ -7979,7 +7979,7 @@
 
 static void battleOp288(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420377c9), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8d67), SkBits2Float(0xc27e6c6d), SkBits2Float(0x4297d744), SkBits2Float(0xc2062b59));
@@ -7994,7 +7994,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc261), SkBits2Float(0x4288e756));
 path.cubicTo(SkBits2Float(0x418c1c95), SkBits2Float(0x42b142a6), SkBits2Float(0xc1ac1f7e), SkBits2Float(0x42af7d4d), SkBits2Float(0xc247fbc6), SkBits2Float(0x4284815d));
 path.cubicTo(SkBits2Float(0xc29cf3e6), SkBits2Float(0x42330ad8), SkBits2Float(0xc2b411b5), SkBits2Float(0x40ef163d), SkBits2Float(0xc29d67bc), SkBits2Float(0xc1d2e345));
@@ -8013,7 +8013,7 @@
 
 static void battleOp289(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8025,7 +8025,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8040,7 +8040,7 @@
 
 static void battleOp290(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8055,7 +8055,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8074,7 +8074,7 @@
 
 static void battleOp291(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8086,7 +8086,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8101,7 +8101,7 @@
 
 static void battleOp292(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8116,7 +8116,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8135,7 +8135,7 @@
 
 static void battleOp293(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8147,7 +8147,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8162,7 +8162,7 @@
 
 static void battleOp294(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8177,7 +8177,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8196,7 +8196,7 @@
 
 static void battleOp295(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8208,7 +8208,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8223,7 +8223,7 @@
 
 static void battleOp296(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8238,7 +8238,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8257,7 +8257,7 @@
 
 static void battleOp297(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8269,7 +8269,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8284,7 +8284,7 @@
 
 static void battleOp298(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8299,7 +8299,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8318,7 +8318,7 @@
 
 static void battleOp299(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8330,7 +8330,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8345,7 +8345,7 @@
 
 static void battleOp300(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8360,7 +8360,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8379,7 +8379,7 @@
 
 static void battleOp301(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8391,7 +8391,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8406,7 +8406,7 @@
 
 static void battleOp302(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8421,7 +8421,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8440,7 +8440,7 @@
 
 static void battleOp303(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8452,7 +8452,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8467,7 +8467,7 @@
 
 static void battleOp304(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8482,7 +8482,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8501,7 +8501,7 @@
 
 static void battleOp305(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8513,7 +8513,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8528,7 +8528,7 @@
 
 static void battleOp306(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8543,7 +8543,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8562,7 +8562,7 @@
 
 static void battleOp307(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8574,7 +8574,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8589,7 +8589,7 @@
 
 static void battleOp308(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8604,7 +8604,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8623,7 +8623,7 @@
 
 static void battleOp309(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8635,7 +8635,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8650,7 +8650,7 @@
 
 static void battleOp310(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8665,7 +8665,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8684,7 +8684,7 @@
 
 static void battleOp311(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8696,7 +8696,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8711,7 +8711,7 @@
 
 static void battleOp312(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8726,7 +8726,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8745,7 +8745,7 @@
 
 static void battleOp313(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8757,7 +8757,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8772,7 +8772,7 @@
 
 static void battleOp314(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8787,7 +8787,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8806,7 +8806,7 @@
 
 static void battleOp315(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8818,7 +8818,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8833,7 +8833,7 @@
 
 static void battleOp316(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8848,7 +8848,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8867,7 +8867,7 @@
 
 static void battleOp317(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8879,7 +8879,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8894,7 +8894,7 @@
 
 static void battleOp318(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8909,7 +8909,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8928,7 +8928,7 @@
 
 static void battleOp319(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -8940,7 +8940,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -8955,7 +8955,7 @@
 
 static void battleOp320(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -8970,7 +8970,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -8989,7 +8989,7 @@
 
 static void battleOp321(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9001,7 +9001,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9016,7 +9016,7 @@
 
 static void battleOp322(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9031,7 +9031,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9050,7 +9050,7 @@
 
 static void battleOp323(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9062,7 +9062,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9077,7 +9077,7 @@
 
 static void battleOp324(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9092,7 +9092,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9111,7 +9111,7 @@
 
 static void battleOp325(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9123,7 +9123,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9138,7 +9138,7 @@
 
 static void battleOp326(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9153,7 +9153,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9172,7 +9172,7 @@
 
 static void battleOp327(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9184,7 +9184,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9199,7 +9199,7 @@
 
 static void battleOp328(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9214,7 +9214,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9233,7 +9233,7 @@
 
 static void battleOp329(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9245,7 +9245,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9260,7 +9260,7 @@
 
 static void battleOp330(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9275,7 +9275,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9294,7 +9294,7 @@
 
 static void battleOp331(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9306,7 +9306,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9321,7 +9321,7 @@
 
 static void battleOp332(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9336,7 +9336,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9355,7 +9355,7 @@
 
 static void battleOp333(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9367,7 +9367,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9382,7 +9382,7 @@
 
 static void battleOp334(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9397,7 +9397,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9416,7 +9416,7 @@
 
 static void battleOp335(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9428,7 +9428,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9443,7 +9443,7 @@
 
 static void battleOp336(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9458,7 +9458,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9477,7 +9477,7 @@
 
 static void battleOp337(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9489,7 +9489,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9504,7 +9504,7 @@
 
 static void battleOp338(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9519,7 +9519,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9538,7 +9538,7 @@
 
 static void battleOp339(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9550,7 +9550,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9565,7 +9565,7 @@
 
 static void battleOp340(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9580,7 +9580,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9599,7 +9599,7 @@
 
 static void battleOp341(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9611,7 +9611,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9626,7 +9626,7 @@
 
 static void battleOp342(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9641,7 +9641,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9660,7 +9660,7 @@
 
 static void battleOp343(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9672,7 +9672,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9687,7 +9687,7 @@
 
 static void battleOp344(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9702,7 +9702,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9721,7 +9721,7 @@
 
 static void battleOp345(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9733,7 +9733,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -9748,7 +9748,7 @@
 
 static void battleOp346(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -9763,7 +9763,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -9782,7 +9782,7 @@
 
 static void battleOp347(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3d570205), SkBits2Float(0xc2a60000), SkBits2Float(0x3dd7026d), SkBits2Float(0xc2a5fffa), SkBits2Float(0x3e2141e6), SkBits2Float(0xc2a5ffed));
 path.lineTo(SkBits2Float(0x3de92565), SkBits2Float(0xc26fffe4));
@@ -9792,7 +9792,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3e214267), SkBits2Float(0xc2a5ffec));
 path.cubicTo(SkBits2Float(0x3e26a1f2), SkBits2Float(0xc2a5ffeb), SkBits2Float(0x3e2c025b), SkBits2Float(0xc2a5ffe9), SkBits2Float(0x3e3162c6), SkBits2Float(0xc2a5ffe7));
 path.lineTo(SkBits2Float(0x3e003af5), SkBits2Float(0xc26fffde));
@@ -9807,7 +9807,7 @@
 
 static void battleOp348(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x365677c0), SkBits2Float(0xc2700002));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3d570205), SkBits2Float(0xc2a60000), SkBits2Float(0x3dd7026d), SkBits2Float(0xc2a5fffa), SkBits2Float(0x3e2141e6), SkBits2Float(0xc2a5ffed));
@@ -9822,7 +9822,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3e3162a4), SkBits2Float(0xc2a5ffe8));
 path.cubicTo(SkBits2Float(0x3e843f51), SkBits2Float(0xc2a5ffd1), SkBits2Float(0x3eafcce9), SkBits2Float(0xc2a5ffa8), SkBits2Float(0x3edb5a6f), SkBits2Float(0xc2a5ff6f));
 path.lineTo(SkBits2Float(0x3e9e9160), SkBits2Float(0xc26fff2e));
@@ -9837,7 +9837,7 @@
 
 static void battleOp349(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e678fda), SkBits2Float(0xc2a60000), SkBits2Float(0x3ee78f7d), SkBits2Float(0xc2a5ff87), SkBits2Float(0x3f2dab18), SkBits2Float(0xc2a5fe96));
 path.lineTo(SkBits2Float(0x3efb15d4), SkBits2Float(0xc26ffdf3));
@@ -9847,7 +9847,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f2daad3), SkBits2Float(0xc2a5fe95));
 path.cubicTo(SkBits2Float(0x3f3374d8), SkBits2Float(0xc2a5fe7b), SkBits2Float(0x3f393eae), SkBits2Float(0xc2a5fe62), SkBits2Float(0x3f3f0885), SkBits2Float(0xc2a5fe46));
 path.lineTo(SkBits2Float(0x3f0a18b8), SkBits2Float(0xc26ffd84));
@@ -9862,7 +9862,7 @@
 
 static void battleOp350(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e678fda), SkBits2Float(0xc2a60000), SkBits2Float(0x3ee78f7d), SkBits2Float(0xc2a5ff87), SkBits2Float(0x3f2dab18), SkBits2Float(0xc2a5fe96));
@@ -9875,7 +9875,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f3f0899), SkBits2Float(0xc2a5fe48));
 path.cubicTo(SkBits2Float(0x3f8e6b81), SkBits2Float(0xc2a5fc98), SkBits2Float(0x3fbd51fb), SkBits2Float(0xc2a5f9aa), SkBits2Float(0x3fec36d3), SkBits2Float(0xc2a5f57e));
 path.lineTo(SkBits2Float(0x3faac1d7), SkBits2Float(0xc26ff0d0));
@@ -9890,7 +9890,7 @@
 
 static void battleOp351(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x403f62fc), SkBits2Float(0xc2a60000), SkBits2Float(0x40bf510b), SkBits2Float(0xc2a5ad41), SkBits2Float(0x410f39cc), SkBits2Float(0xc2a50821));
 path.lineTo(SkBits2Float(0x40cf12cc), SkBits2Float(0xc26e99a0));
@@ -9900,7 +9900,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x410f39cd), SkBits2Float(0xc2a50820));
 path.cubicTo(SkBits2Float(0x4113fb3b), SkBits2Float(0xc2a4f79d), SkBits2Float(0x4118bbf1), SkBits2Float(0xc2a4e648), SkBits2Float(0x411d7be1), SkBits2Float(0xc2a4d421));
 path.lineTo(SkBits2Float(0x40e3b008), SkBits2Float(0xc26e4e75));
@@ -9914,7 +9914,7 @@
 
 static void battleOp352(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e0b17a8), SkBits2Float(0xc2a60000), SkBits2Float(0x3e8b179e), SkBits2Float(0xc2a5ffd4), SkBits2Float(0x3ed0a337), SkBits2Float(0xc2a5ff7c));
@@ -9928,7 +9928,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee58048), SkBits2Float(0xc2a5ff61));
 path.cubicTo(SkBits2Float(0x3f2b1987), SkBits2Float(0xc2a5fec4), SkBits2Float(0x3f637253), SkBits2Float(0xc2a5fdb6), SkBits2Float(0x3f8de535), SkBits2Float(0xc2a5fc35));
 path.lineTo(SkBits2Float(0x3f4d269a), SkBits2Float(0xc26ffa85));
@@ -9942,7 +9942,7 @@
 
 static void battleOp1390(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xb7240057), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x420377ff), SkBits2Float(0xc2a5ffff), SkBits2Float(0x427a8dc0), SkBits2Float(0xc27e6c2f), SkBits2Float(0x4297d760), SkBits2Float(0xc2062ad2));
@@ -9956,7 +9956,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc156), SkBits2Float(0x4288e7b2));
 path.cubicTo(SkBits2Float(0x418c1984), SkBits2Float(0x42b142da), SkBits2Float(0xc1ac2314), SkBits2Float(0x42af7d21), SkBits2Float(0xc247fd43), SkBits2Float(0x428480ce));
 path.cubicTo(SkBits2Float(0xc29cf47f), SkBits2Float(0x423308f3), SkBits2Float(0xc2b411dd), SkBits2Float(0x40ef0242), SkBits2Float(0xc29d6757), SkBits2Float(0xc1d2e807));
@@ -9975,7 +9975,7 @@
 
 static void battleOp1391(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -9987,7 +9987,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -10002,7 +10002,7 @@
 
 static void battleOp1392(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10017,7 +10017,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10036,7 +10036,7 @@
 
 static void battleOp1393(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c436965), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3cc36072), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d128619), SkBits2Float(0xc2a5fffe));
 path.lineTo(SkBits2Float(0x3cd3db06), SkBits2Float(0xc26fffff));
@@ -10046,7 +10046,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d12888d), SkBits2Float(0xc2a5ffff));
 path.cubicTo(SkBits2Float(0x3d176d55), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d1c4dcb), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d212e40), SkBits2Float(0xc2a5ffff));
 path.lineTo(SkBits2Float(0x3ce90a84), SkBits2Float(0xc26ffffe));
@@ -10061,7 +10061,7 @@
 
 static void battleOp1394(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x36606a00), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c436965), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3cc36072), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d128619), SkBits2Float(0xc2a5fffe));
@@ -10074,7 +10074,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d212fd0), SkBits2Float(0xc2a5ffff));
 path.cubicTo(SkBits2Float(0x3d705530), SkBits2Float(0xc2a5fffe), SkBits2Float(0x3d9fbf82), SkBits2Float(0xc2a5fffc), SkBits2Float(0x3dc7546b), SkBits2Float(0xc2a5fffa));
 path.lineTo(SkBits2Float(0x3d901696), SkBits2Float(0xc26ffff5));
@@ -10089,7 +10089,7 @@
 
 static void battleOp1395(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e06023f), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e860192), SkBits2Float(0xc2a5ffd6), SkBits2Float(0x3ec901db), SkBits2Float(0xc2a5ff85));
 path.lineTo(SkBits2Float(0x3e914e16), SkBits2Float(0xc26fff50));
@@ -10099,7 +10099,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ec9015b), SkBits2Float(0xc2a5ff86));
 path.cubicTo(SkBits2Float(0x3ecfb4f0), SkBits2Float(0xc2a5ff7d), SkBits2Float(0x3ed66842), SkBits2Float(0xc2a5ff75), SkBits2Float(0x3edd1b92), SkBits2Float(0xc2a5ff6c));
 path.lineTo(SkBits2Float(0x3e9fd5de), SkBits2Float(0xc26fff2b));
@@ -10114,7 +10114,7 @@
 
 static void battleOp1396(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e0601e9), SkBits2Float(0xc2a60000), SkBits2Float(0x3e86013c), SkBits2Float(0xc2a5ffd6), SkBits2Float(0x3ec9015a), SkBits2Float(0xc2a5ff85));
@@ -10127,7 +10127,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3edd1b0d), SkBits2Float(0xc2a5ff6d));
 path.cubicTo(SkBits2Float(0x3f24d70e), SkBits2Float(0xc2a5fedc), SkBits2Float(0x3f5b204e), SkBits2Float(0xc2a5fde1), SkBits2Float(0x3f88b475), SkBits2Float(0xc2a5fc7b));
 path.lineTo(SkBits2Float(0x3f45a57e), SkBits2Float(0xc26ffaea));
@@ -10141,7 +10141,7 @@
 
 static void battleOp2193(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e3881bc), SkBits2Float(0xc2a60000), SkBits2Float(0x3eb88238), SkBits2Float(0xc2a5ffb3), SkBits2Float(0x3f0a6190), SkBits2Float(0xc2a5ff19));
 path.lineTo(SkBits2Float(0x3ec8119b), SkBits2Float(0xc26ffeb2));
@@ -10151,7 +10151,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f0a6183), SkBits2Float(0xc2a5ff19));
 path.cubicTo(SkBits2Float(0x3f0efe46), SkBits2Float(0xc2a5ff0a), SkBits2Float(0x3f139b44), SkBits2Float(0xc2a5fef9), SkBits2Float(0x3f183842), SkBits2Float(0xc2a5fee9));
 path.lineTo(SkBits2Float(0x3edc1349), SkBits2Float(0xc26ffe6c));
@@ -10166,7 +10166,7 @@
 
 static void battleOp2194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3629eed0), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e3881ab), SkBits2Float(0xc2a60000), SkBits2Float(0x3eb88227), SkBits2Float(0xc2a5ffb3), SkBits2Float(0x3f0a6183), SkBits2Float(0xc2a5ff19));
@@ -10180,7 +10180,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f183800), SkBits2Float(0xc2a5fee9));
 path.cubicTo(SkBits2Float(0x3f62f7a2), SkBits2Float(0xc2a5fdd7), SkBits2Float(0x3f96db12), SkBits2Float(0xc2a5fbfa), SkBits2Float(0x3fbc3981), SkBits2Float(0xc2a5f954));
 path.lineTo(SkBits2Float(0x3f8810cc), SkBits2Float(0xc26ff65b));
@@ -10194,7 +10194,7 @@
 
 static void battleOp3368(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10209,7 +10209,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10228,7 +10228,7 @@
 
 static void battleOp3369(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -10240,7 +10240,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -10255,7 +10255,7 @@
 
 static void battleOp3370(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10270,7 +10270,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10289,7 +10289,7 @@
 
 static void battleOp3371(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c85f8a2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d05fda5), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d48fefa), SkBits2Float(0xc2a5fffd));
 path.lineTo(SkBits2Float(0x3d114e3a), SkBits2Float(0xc26ffffd));
@@ -10299,7 +10299,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d49018c), SkBits2Float(0xc2a5fffe));
 path.cubicTo(SkBits2Float(0x3d4fb7df), SkBits2Float(0xc2a5fffd), SkBits2Float(0x3d5667bf), SkBits2Float(0xc2a5fffd), SkBits2Float(0x3d5d179f), SkBits2Float(0xc2a5fffd));
 path.lineTo(SkBits2Float(0x3d1fd60d), SkBits2Float(0xc26ffffd));
@@ -10314,7 +10314,7 @@
 
 static void battleOp3372(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c85f8a2), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d05fda5), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d48fefa), SkBits2Float(0xc2a5fffd));
@@ -10328,7 +10328,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d5d1b4e), SkBits2Float(0xc2a5fffe));
 path.cubicTo(SkBits2Float(0x3da4d661), SkBits2Float(0xc2a5fffc), SkBits2Float(0x3ddb1fb1), SkBits2Float(0xc2a5fff8), SkBits2Float(0x3e08b47e), SkBits2Float(0xc2a5fff2));
 path.lineTo(SkBits2Float(0x3dc5a6e0), SkBits2Float(0xc26fffec));
@@ -10342,7 +10342,7 @@
 
 static void battleOp4290(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10357,7 +10357,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10376,7 +10376,7 @@
 
 static void battleOp4291(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -10388,7 +10388,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -10403,7 +10403,7 @@
 
 static void battleOp4292(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10418,7 +10418,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10437,7 +10437,7 @@
 
 static void battleOp4293(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
 path.cubicTo(SkBits2Float(0x42b267e8), SkBits2Float(0xc05e90e8), SkBits2Float(0x42a6fcc7), SkBits2Float(0x41fcbc94), SkBits2Float(0x42757227), SkBits2Float(0x425f9011));
@@ -10449,7 +10449,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42757226), SkBits2Float(0x425f9012));
 path.cubicTo(SkBits2Float(0x42643732), SkBits2Float(0x42727ac8), SkBits2Float(0x4250db30), SkBits2Float(0x4281abaa), SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.lineTo(SkBits2Float(0x4207b9a6), SkBits2Float(0x4245efa0));
@@ -10464,7 +10464,7 @@
 
 static void battleOp4294(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x42037818), SkBits2Float(0xc2a60000), SkBits2Float(0x427a8dee), SkBits2Float(0xc27e6c10), SkBits2Float(0x4297d76f), SkBits2Float(0xc2062a8f));
@@ -10479,7 +10479,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x423bc0d1), SkBits2Float(0x4288e7e0));
 path.cubicTo(SkBits2Float(0x418c17fd), SkBits2Float(0x42b142f1), SkBits2Float(0xc1ac24e4), SkBits2Float(0x42af7d09), SkBits2Float(0xc247fe03), SkBits2Float(0x42848083));
 path.cubicTo(SkBits2Float(0xc29cf4c9), SkBits2Float(0x423307fa), SkBits2Float(0xc2b411ee), SkBits2Float(0x40eef84a), SkBits2Float(0xc29d6723), SkBits2Float(0xc1d2ea61));
@@ -10498,7 +10498,7 @@
 
 static void battleOp4295(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e3881bc), SkBits2Float(0xc2a60000), SkBits2Float(0x3eb88238), SkBits2Float(0xc2a5ffb3), SkBits2Float(0x3f0a6190), SkBits2Float(0xc2a5ff19));
 path.lineTo(SkBits2Float(0x3ec8119b), SkBits2Float(0xc26ffeb2));
@@ -10508,7 +10508,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f0a6183), SkBits2Float(0xc2a5ff19));
 path.cubicTo(SkBits2Float(0x3f0efe46), SkBits2Float(0xc2a5ff0a), SkBits2Float(0x3f139b44), SkBits2Float(0xc2a5fef9), SkBits2Float(0x3f183842), SkBits2Float(0xc2a5fee9));
 path.lineTo(SkBits2Float(0x3edc1349), SkBits2Float(0xc26ffe6c));
@@ -10523,7 +10523,7 @@
 
 static void battleOp4296(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3629eed0), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e3881ab), SkBits2Float(0xc2a60000), SkBits2Float(0x3eb88227), SkBits2Float(0xc2a5ffb3), SkBits2Float(0x3f0a6183), SkBits2Float(0xc2a5ff19));
@@ -10537,7 +10537,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f183800), SkBits2Float(0xc2a5fee9));
 path.cubicTo(SkBits2Float(0x3f62f7a2), SkBits2Float(0xc2a5fdd7), SkBits2Float(0x3f96db12), SkBits2Float(0xc2a5fbfa), SkBits2Float(0x3fbc3981), SkBits2Float(0xc2a5f954));
 path.lineTo(SkBits2Float(0x3f8810cc), SkBits2Float(0xc26ff65b));
@@ -10551,7 +10551,7 @@
 
 static void battleOp5193(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e0b17ea), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3e8b17df), SkBits2Float(0xc2a5ffd4), SkBits2Float(0x3ed0a399), SkBits2Float(0xc2a5ff7c));
 path.lineTo(SkBits2Float(0x3e96d285), SkBits2Float(0xc26fff42));
@@ -10561,7 +10561,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ed0a338), SkBits2Float(0xc2a5ff7d));
 path.cubicTo(SkBits2Float(0x3ed797a0), SkBits2Float(0xc2a5ff73), SkBits2Float(0x3ede8c36), SkBits2Float(0xc2a5ff6a), SkBits2Float(0x3ee580cb), SkBits2Float(0xc2a5ff60));
 path.lineTo(SkBits2Float(0x3ea5e78a), SkBits2Float(0xc26fff1b));
@@ -10576,7 +10576,7 @@
 
 static void battleOp5194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e0b17a8), SkBits2Float(0xc2a60000), SkBits2Float(0x3e8b179e), SkBits2Float(0xc2a5ffd4), SkBits2Float(0x3ed0a337), SkBits2Float(0xc2a5ff7c));
@@ -10590,7 +10590,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee58048), SkBits2Float(0xc2a5ff61));
 path.cubicTo(SkBits2Float(0x3f2b1987), SkBits2Float(0xc2a5fec4), SkBits2Float(0x3f637253), SkBits2Float(0xc2a5fdb6), SkBits2Float(0x3f8de535), SkBits2Float(0xc2a5fc35));
 path.lineTo(SkBits2Float(0x3f4d269a), SkBits2Float(0xc26ffa85));
@@ -10604,7 +10604,7 @@
 
 static void battleOp402(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc2700000));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3e0b17a8), SkBits2Float(0xc2a60000), SkBits2Float(0x3e8b179e), SkBits2Float(0xc2a5ffd4), SkBits2Float(0x3ed0a337), SkBits2Float(0xc2a5ff7c));
@@ -10618,7 +10618,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ee58048), SkBits2Float(0xc2a5ff61));
 path.cubicTo(SkBits2Float(0x3f2b1987), SkBits2Float(0xc2a5fec4), SkBits2Float(0x3f637253), SkBits2Float(0xc2a5fdb6), SkBits2Float(0x3f8de535), SkBits2Float(0xc2a5fc35));
 path.lineTo(SkBits2Float(0x3f4d269a), SkBits2Float(0xc26ffa85));
@@ -10632,7 +10632,7 @@
 
 static void battleOp6000(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x27b71bcd), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c9b2383), SkBits2Float(0xc2a60000), SkBits2Float(0x3d1b200b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d68ae54), SkBits2Float(0xc2a5fffd));
 path.lineTo(SkBits2Float(0x3d283599), SkBits2Float(0xc26ffffc));
@@ -10642,7 +10642,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d68b08b), SkBits2Float(0xc2a5fffd));
 path.cubicTo(SkBits2Float(0x3d707589), SkBits2Float(0xc2a5fffd), SkBits2Float(0x3d783329), SkBits2Float(0xc2a5fffd), SkBits2Float(0x3d7ff0c9), SkBits2Float(0xc2a5fffd));
 path.lineTo(SkBits2Float(0x3d3907c2), SkBits2Float(0xc26ffffc));
@@ -10656,7 +10656,7 @@
 
 static void battleOp6001(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0xc26fffff));
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xc2a60000));
 path.cubicTo(SkBits2Float(0x3c9b2383), SkBits2Float(0xc2a60000), SkBits2Float(0x3d1b200b), SkBits2Float(0xc2a5ffff), SkBits2Float(0x3d68ae54), SkBits2Float(0xc2a5fffd));
@@ -10669,7 +10669,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3d7ff566), SkBits2Float(0xc2a5fffd));
 path.cubicTo(SkBits2Float(0x3dbed1a5), SkBits2Float(0xc2a5fffa), SkBits2Float(0x3dfda9cc), SkBits2Float(0xc2a5fff4), SkBits2Float(0x3e1e40f8), SkBits2Float(0xc2a5ffed));
 path.lineTo(SkBits2Float(0x3de4ce81), SkBits2Float(0xc26fffe5));
diff --git a/tests/PathOpsBuildUseTest.cpp b/tests/PathOpsBuildUseTest.cpp
index 4ad239d..da17067 100644
--- a/tests/PathOpsBuildUseTest.cpp
+++ b/tests/PathOpsBuildUseTest.cpp
@@ -13,7 +13,6 @@
     SkOpBuilder builder;
     SkPath path;
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43adff26));  // 234.901f, 347.993f
 path.quadTo(SkBits2Float(0x436ae68e), SkBits2Float(0x43b32ca2), SkBits2Float(0x4363940a), SkBits2Float(0x43b6d5e4));  // 234.901f, 358.349f, 227.578f, 365.671f
 path.quadTo(SkBits2Float(0x435c4186), SkBits2Float(0x43ba7f26), SkBits2Float(0x4351e68e), SkBits2Float(0x43ba7f26));  // 220.256f, 372.993f, 209.901f, 372.993f
@@ -28,7 +27,6 @@
     builder.add(path0, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x432a0e2c));  // 346.084f, 170.055f
 path.quadTo(SkBits2Float(0x43ad0aca), SkBits2Float(0x43346924), SkBits2Float(0x43a96188), SkBits2Float(0x433bbba8));  // 346.084f, 180.411f, 338.762f, 187.733f
 path.quadTo(SkBits2Float(0x43a5b846), SkBits2Float(0x43430e2c), SkBits2Float(0x43a08aca), SkBits2Float(0x43430e2c));  // 331.44f, 195.055f, 321.084f, 195.055f
@@ -43,7 +41,6 @@
     builder.add(path1, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x431f14f9), SkBits2Float(0x433943fd));  // 159.082f, 185.266f
 path.quadTo(SkBits2Float(0x431f14f9), SkBits2Float(0x43439ef4), SkBits2Float(0x4317c275), SkBits2Float(0x434af179));  // 159.082f, 195.621f, 151.76f, 202.943f
 path.quadTo(SkBits2Float(0x43106ff0), SkBits2Float(0x435243fd), SkBits2Float(0x430614f9), SkBits2Float(0x435243fd));  // 144.437f, 210.266f, 134.082f, 210.266f
@@ -58,7 +55,6 @@
     builder.add(path2, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43abd585));  // 186.838f, 343.668f
 path.quadTo(SkBits2Float(0x433ad67a), SkBits2Float(0x43b10301), SkBits2Float(0x433383f6), SkBits2Float(0x43b4ac43));  // 186.838f, 354.023f, 179.515f, 361.346f
 path.quadTo(SkBits2Float(0x432c3172), SkBits2Float(0x43b85585), SkBits2Float(0x4321d67a), SkBits2Float(0x43b85585));  // 172.193f, 368.668f, 161.838f, 368.668f
@@ -73,7 +69,7 @@
     builder.add(path3, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43bff91b), SkBits2Float(0x43973a57));  // 383.946f, 302.456f
 path.quadTo(SkBits2Float(0x43bff91b), SkBits2Float(0x439c67d3), SkBits2Float(0x43bc4fd9), SkBits2Float(0x43a01115));  // 383.946f, 312.811f, 376.624f, 320.133f
 path.quadTo(SkBits2Float(0x43b8a697), SkBits2Float(0x43a3ba57), SkBits2Float(0x43b3791b), SkBits2Float(0x43a3ba57));  // 369.301f, 327.456f, 358.946f, 327.456f
@@ -88,7 +84,7 @@
     builder.add(path4, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43374c2c), SkBits2Float(0x437e8a30));  // 183.298f, 254.54f
 path.quadTo(SkBits2Float(0x43374c2c), SkBits2Float(0x43847294), SkBits2Float(0x432ff9a8), SkBits2Float(0x43881bd6));  // 183.298f, 264.895f, 175.975f, 272.217f
 path.quadTo(SkBits2Float(0x4328a724), SkBits2Float(0x438bc518), SkBits2Float(0x431e4c2c), SkBits2Float(0x438bc518));  // 168.653f, 279.54f, 158.298f, 279.54f
@@ -103,7 +99,7 @@
     builder.add(path5, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x430e01e8), SkBits2Float(0x435c8671));  // 142.007f, 220.525f
 path.quadTo(SkBits2Float(0x430e01e8), SkBits2Float(0x4366e168), SkBits2Float(0x4306af64), SkBits2Float(0x436e33ed));  // 142.007f, 230.88f, 134.685f, 238.203f
 path.quadTo(SkBits2Float(0x42feb9bf), SkBits2Float(0x43758671), SkBits2Float(0x42ea03d0), SkBits2Float(0x43758671));  // 127.363f, 245.525f, 117.007f, 245.525f
@@ -118,7 +114,7 @@
     builder.add(path6, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x438b7062), SkBits2Float(0x42d54bf2));  // 278.878f, 106.648f
 path.quadTo(SkBits2Float(0x438b7062), SkBits2Float(0x42ea01e1), SkBits2Float(0x4387c720), SkBits2Float(0x42f8a6ea));  // 278.878f, 117.004f, 271.556f, 124.326f
 path.quadTo(SkBits2Float(0x43841dde), SkBits2Float(0x4303a5f9), SkBits2Float(0x437de0c4), SkBits2Float(0x4303a5f9));  // 264.233f, 131.648f, 253.878f, 131.648f
@@ -133,7 +129,7 @@
     builder.add(path7, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x43963745));  // 444.5f, 300.432f
 path.quadTo(SkBits2Float(0x43de3ff6), SkBits2Float(0x439b64c1), SkBits2Float(0x43da96b4), SkBits2Float(0x439f0e03));  // 444.5f, 310.787f, 437.177f, 318.109f
 path.quadTo(SkBits2Float(0x43d6ed72), SkBits2Float(0x43a2b745), SkBits2Float(0x43d1bff6), SkBits2Float(0x43a2b745));  // 429.855f, 325.432f, 419.5f, 325.432f
@@ -148,7 +144,7 @@
     builder.add(path8, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43aae79c), SkBits2Float(0x438d0cbc));  // 341.809f, 282.099f
 path.quadTo(SkBits2Float(0x43aae79c), SkBits2Float(0x43923a38), SkBits2Float(0x43a73e5a), SkBits2Float(0x4395e37a));  // 341.809f, 292.455f, 334.487f, 299.777f
 path.quadTo(SkBits2Float(0x43a39518), SkBits2Float(0x43998cbc), SkBits2Float(0x439e679c), SkBits2Float(0x43998cbc));  // 327.165f, 307.099f, 316.809f, 307.099f
@@ -163,7 +159,7 @@
     builder.add(path9, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43842ec9));  // 212.807f, 264.366f
 path.quadTo(SkBits2Float(0x4354ce7d), SkBits2Float(0x43895c45), SkBits2Float(0x434d7bf9), SkBits2Float(0x438d0587));  // 212.807f, 274.721f, 205.484f, 282.043f
 path.quadTo(SkBits2Float(0x43462974), SkBits2Float(0x4390aec9), SkBits2Float(0x433bce7d), SkBits2Float(0x4390aec9));  // 198.162f, 289.366f, 187.807f, 289.366f
@@ -178,7 +174,7 @@
     builder.add(path10, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a8c299), SkBits2Float(0x432fce08));  // 337.52f, 175.805f
 path.quadTo(SkBits2Float(0x43a8c299), SkBits2Float(0x433a2900), SkBits2Float(0x43a51957), SkBits2Float(0x43417b84));  // 337.52f, 186.16f, 330.198f, 193.482f
 path.quadTo(SkBits2Float(0x43a17015), SkBits2Float(0x4348ce08), SkBits2Float(0x439c4299), SkBits2Float(0x4348ce08));  // 322.876f, 200.805f, 312.52f, 200.805f
@@ -193,7 +189,7 @@
     builder.add(path11, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43d7486e), SkBits2Float(0x430ebc47));  // 430.566f, 142.735f
 path.quadTo(SkBits2Float(0x43d7486e), SkBits2Float(0x4319173e), SkBits2Float(0x43d39f2c), SkBits2Float(0x432069c3));  // 430.566f, 153.091f, 423.244f, 160.413f
 path.quadTo(SkBits2Float(0x43cff5ea), SkBits2Float(0x4327bc47), SkBits2Float(0x43cac86e), SkBits2Float(0x4327bc47));  // 415.921f, 167.735f, 405.566f, 167.735f
@@ -208,7 +204,7 @@
     builder.add(path12, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43484ac4), SkBits2Float(0x43421f09));  // 200.292f, 194.121f
 path.quadTo(SkBits2Float(0x43484ac4), SkBits2Float(0x434c7a00), SkBits2Float(0x4340f840), SkBits2Float(0x4353cc85));  // 200.292f, 204.477f, 192.97f, 211.799f
 path.quadTo(SkBits2Float(0x4339a5bc), SkBits2Float(0x435b1f09), SkBits2Float(0x432f4ac4), SkBits2Float(0x435b1f09));  // 185.647f, 219.121f, 175.292f, 219.121f
@@ -223,7 +219,7 @@
     builder.add(path13, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4328883e), SkBits2Float(0x42fb0be0));  // 168.532f, 125.523f
 path.quadTo(SkBits2Float(0x4328883e), SkBits2Float(0x4307e0e7), SkBits2Float(0x432135ba), SkBits2Float(0x430f336c));  // 168.532f, 135.879f, 161.21f, 143.201f
 path.quadTo(SkBits2Float(0x4319e336), SkBits2Float(0x431685f0), SkBits2Float(0x430f883e), SkBits2Float(0x431685f0));  // 153.888f, 150.523f, 143.532f, 150.523f
@@ -238,7 +234,7 @@
     builder.add(path14, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x439bb140));  // 357.5f, 311.385f
 path.quadTo(SkBits2Float(0x43b2bff8), SkBits2Float(0x43a0debc), SkBits2Float(0x43af16b6), SkBits2Float(0x43a487fe));  // 357.5f, 321.74f, 350.177f, 329.062f
 path.quadTo(SkBits2Float(0x43ab6d74), SkBits2Float(0x43a83140), SkBits2Float(0x43a63ff8), SkBits2Float(0x43a83140));  // 342.855f, 336.385f, 332.5f, 336.385f
@@ -253,7 +249,7 @@
     builder.add(path15, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435ae426), SkBits2Float(0x4341f066));  // 218.891f, 193.939f
 path.quadTo(SkBits2Float(0x435ae426), SkBits2Float(0x434c4b5e), SkBits2Float(0x435391a2), SkBits2Float(0x43539de2));  // 218.891f, 204.294f, 211.569f, 211.617f
 path.quadTo(SkBits2Float(0x434c3f1e), SkBits2Float(0x435af066), SkBits2Float(0x4341e426), SkBits2Float(0x435af066));  // 204.247f, 218.939f, 193.891f, 218.939f
@@ -268,7 +264,7 @@
     builder.add(path16, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439817ba), SkBits2Float(0x42e83ba4));  // 304.185f, 116.116f
 path.quadTo(SkBits2Float(0x439817ba), SkBits2Float(0x42fcf193), SkBits2Float(0x43946e78), SkBits2Float(0x4305cb4e));  // 304.185f, 126.472f, 296.863f, 133.794f
 path.quadTo(SkBits2Float(0x4390c536), SkBits2Float(0x430d1dd2), SkBits2Float(0x438b97ba), SkBits2Float(0x430d1dd2));  // 289.541f, 141.116f, 279.185f, 141.116f
@@ -283,7 +279,7 @@
     builder.add(path17, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4350558c), SkBits2Float(0x4382efb0));  // 208.334f, 261.873f
 path.quadTo(SkBits2Float(0x4350558c), SkBits2Float(0x43881d2c), SkBits2Float(0x43490308), SkBits2Float(0x438bc66e));  // 208.334f, 272.228f, 201.012f, 279.55f
 path.quadTo(SkBits2Float(0x4341b084), SkBits2Float(0x438f6fb0), SkBits2Float(0x4337558c), SkBits2Float(0x438f6fb0));  // 193.69f, 286.873f, 183.334f, 286.873f
@@ -298,7 +294,7 @@
     builder.add(path18, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43a51083));  // 84.4611f, 330.129f
 path.quadTo(SkBits2Float(0x42a8ec1a), SkBits2Float(0x43aa3dff), SkBits2Float(0x429a4711), SkBits2Float(0x43ade741));  // 84.4611f, 340.484f, 77.1388f, 347.807f
 path.quadTo(SkBits2Float(0x428ba209), SkBits2Float(0x43b19083), SkBits2Float(0x426dd834), SkBits2Float(0x43b19083));  // 69.8165f, 355.129f, 59.4611f, 355.129f
@@ -313,7 +309,7 @@
     builder.add(path19, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x438f0f1d));  // 345.297f, 286.118f
 path.quadTo(SkBits2Float(0x43aca5fa), SkBits2Float(0x43943c99), SkBits2Float(0x43a8fcb8), SkBits2Float(0x4397e5db));  // 345.297f, 296.473f, 337.974f, 303.796f
 path.quadTo(SkBits2Float(0x43a55376), SkBits2Float(0x439b8f1d), SkBits2Float(0x43a025fa), SkBits2Float(0x439b8f1d));  // 330.652f, 311.118f, 320.297f, 311.118f
@@ -328,7 +324,7 @@
     builder.add(path20, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43846cfc), SkBits2Float(0x431b61f0));  // 264.851f, 155.383f
 path.quadTo(SkBits2Float(0x43846cfc), SkBits2Float(0x4325bce8), SkBits2Float(0x4380c3ba), SkBits2Float(0x432d0f6c));  // 264.851f, 165.738f, 257.529f, 173.06f
 path.quadTo(SkBits2Float(0x437a34f0), SkBits2Float(0x433461f0), SkBits2Float(0x436fd9f8), SkBits2Float(0x433461f0));  // 250.207f, 180.383f, 239.851f, 180.383f
@@ -343,7 +339,7 @@
     builder.add(path21, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439597be), SkBits2Float(0x438dc3e1));  // 299.185f, 283.53f
 path.quadTo(SkBits2Float(0x439597be), SkBits2Float(0x4392f15d), SkBits2Float(0x4391ee7c), SkBits2Float(0x43969a9f));  // 299.185f, 293.886f, 291.863f, 301.208f
 path.quadTo(SkBits2Float(0x438e453a), SkBits2Float(0x439a43e1), SkBits2Float(0x438917be), SkBits2Float(0x439a43e1));  // 284.541f, 308.53f, 274.185f, 308.53f
@@ -358,7 +354,7 @@
     builder.add(path22, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x43699568));  // 109.739f, 233.584f
 path.quadTo(SkBits2Float(0x42db7a2b), SkBits2Float(0x4373f05f), SkBits2Float(0x42ccd522), SkBits2Float(0x437b42e3));  // 109.739f, 243.939f, 102.416f, 251.261f
 path.quadTo(SkBits2Float(0x42be301a), SkBits2Float(0x43814ab4), SkBits2Float(0x42a97a2b), SkBits2Float(0x43814ab4));  // 95.0939f, 258.584f, 84.7386f, 258.584f
@@ -373,7 +369,7 @@
     builder.add(path23, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x433bea80), SkBits2Float(0x43861662));  // 187.916f, 268.175f
 path.quadTo(SkBits2Float(0x433bea80), SkBits2Float(0x438b43de), SkBits2Float(0x433497fc), SkBits2Float(0x438eed20));  // 187.916f, 278.53f, 180.594f, 285.853f
 path.quadTo(SkBits2Float(0x432d4578), SkBits2Float(0x43929662), SkBits2Float(0x4322ea80), SkBits2Float(0x43929662));  // 173.271f, 293.175f, 162.916f, 293.175f
@@ -388,7 +384,7 @@
     builder.add(path24, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4386aaee), SkBits2Float(0x43991356));  // 269.335f, 306.151f
 path.quadTo(SkBits2Float(0x4386aaee), SkBits2Float(0x439e40d2), SkBits2Float(0x438301ac), SkBits2Float(0x43a1ea14));  // 269.335f, 316.506f, 262.013f, 323.829f
 path.quadTo(SkBits2Float(0x437eb0d4), SkBits2Float(0x43a59356), SkBits2Float(0x437455dc), SkBits2Float(0x43a59356));  // 254.691f, 331.151f, 244.335f, 331.151f
@@ -403,7 +399,7 @@
     builder.add(path25, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42d6b25c));  // 353.31f, 107.348f
 path.quadTo(SkBits2Float(0x43b0a7ab), SkBits2Float(0x42eb684b), SkBits2Float(0x43acfe69), SkBits2Float(0x42fa0d53));  // 353.31f, 117.704f, 345.988f, 125.026f
 path.quadTo(SkBits2Float(0x43a95527), SkBits2Float(0x4304592e), SkBits2Float(0x43a427ab), SkBits2Float(0x4304592e));  // 338.665f, 132.348f, 328.31f, 132.348f
@@ -418,7 +414,7 @@
     builder.add(path26, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43d478db), SkBits2Float(0x4301c45a));  // 424.944f, 129.767f
 path.quadTo(SkBits2Float(0x43d478db), SkBits2Float(0x430c1f52), SkBits2Float(0x43d0cf99), SkBits2Float(0x431371d6));  // 424.944f, 140.122f, 417.622f, 147.445f
 path.quadTo(SkBits2Float(0x43cd2657), SkBits2Float(0x431ac45a), SkBits2Float(0x43c7f8db), SkBits2Float(0x431ac45a));  // 410.3f, 154.767f, 399.944f, 154.767f
@@ -433,7 +429,7 @@
     builder.add(path27, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4370d681), SkBits2Float(0x4375fc44));  // 240.838f, 245.985f
 path.quadTo(SkBits2Float(0x4370d681), SkBits2Float(0x43802b9e), SkBits2Float(0x436983fd), SkBits2Float(0x4383d4e0));  // 240.838f, 256.341f, 233.516f, 263.663f
 path.quadTo(SkBits2Float(0x43623178), SkBits2Float(0x43877e22), SkBits2Float(0x4357d681), SkBits2Float(0x43877e22));  // 226.193f, 270.985f, 215.838f, 270.985f
@@ -448,7 +444,7 @@
     builder.add(path28, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438a9227));  // 272.47f, 277.142f
 path.quadTo(SkBits2Float(0x43883c1d), SkBits2Float(0x438fbfa3), SkBits2Float(0x438492db), SkBits2Float(0x439368e5));  // 272.47f, 287.497f, 265.147f, 294.819f
 path.quadTo(SkBits2Float(0x4380e999), SkBits2Float(0x43971227), SkBits2Float(0x4377783a), SkBits2Float(0x43971227));  // 257.825f, 302.142f, 247.47f, 302.142f
@@ -463,7 +459,7 @@
     builder.add(path29, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a2dbb0));  // 363.403f, 325.716f
 path.quadTo(SkBits2Float(0x43b5b3a4), SkBits2Float(0x43a8092c), SkBits2Float(0x43b20a62), SkBits2Float(0x43abb26e));  // 363.403f, 336.072f, 356.081f, 343.394f
 path.quadTo(SkBits2Float(0x43ae6120), SkBits2Float(0x43af5bb0), SkBits2Float(0x43a933a4), SkBits2Float(0x43af5bb0));  // 348.759f, 350.716f, 338.403f, 350.716f
@@ -478,7 +474,7 @@
     builder.add(path30, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x431b2abc));  // 336.226f, 155.167f
 path.quadTo(SkBits2Float(0x43a81cf4), SkBits2Float(0x432585b4), SkBits2Float(0x43a473b2), SkBits2Float(0x432cd838));  // 336.226f, 165.522f, 328.904f, 172.845f
 path.quadTo(SkBits2Float(0x43a0ca70), SkBits2Float(0x43342abc), SkBits2Float(0x439b9cf4), SkBits2Float(0x43342abc));  // 321.582f, 180.167f, 311.226f, 180.167f
@@ -493,7 +489,7 @@
     builder.add(path31, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435e092f), SkBits2Float(0x43698168));  // 222.036f, 233.505f
 path.quadTo(SkBits2Float(0x435e092f), SkBits2Float(0x4373dc5f), SkBits2Float(0x4356b6ab), SkBits2Float(0x437b2ee3));  // 222.036f, 243.861f, 214.714f, 251.183f
 path.quadTo(SkBits2Float(0x434f6426), SkBits2Float(0x438140b4), SkBits2Float(0x4345092f), SkBits2Float(0x438140b4));  // 207.391f, 258.505f, 197.036f, 258.505f
@@ -508,7 +504,7 @@
     builder.add(path32, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b29d51), SkBits2Float(0x434f504b));  // 357.229f, 207.314f
 path.quadTo(SkBits2Float(0x43b29d51), SkBits2Float(0x4359ab42), SkBits2Float(0x43aef40f), SkBits2Float(0x4360fdc7));  // 357.229f, 217.669f, 349.907f, 224.991f
 path.quadTo(SkBits2Float(0x43ab4acd), SkBits2Float(0x4368504b), SkBits2Float(0x43a61d51), SkBits2Float(0x4368504b));  // 342.584f, 232.314f, 332.229f, 232.314f
@@ -523,7 +519,7 @@
     builder.add(path33, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439022b6), SkBits2Float(0x434132a3));  // 288.271f, 193.198f
 path.quadTo(SkBits2Float(0x439022b6), SkBits2Float(0x434b8d9a), SkBits2Float(0x438c7974), SkBits2Float(0x4352e01f));  // 288.271f, 203.553f, 280.949f, 210.875f
 path.quadTo(SkBits2Float(0x4388d032), SkBits2Float(0x435a32a3), SkBits2Float(0x4383a2b6), SkBits2Float(0x435a32a3));  // 273.627f, 218.198f, 263.271f, 218.198f
@@ -538,7 +534,7 @@
     builder.add(path34, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x4386bd38));  // 204.43f, 269.478f
 path.quadTo(SkBits2Float(0x434c6e1b), SkBits2Float(0x438beab4), SkBits2Float(0x43451b97), SkBits2Float(0x438f93f6));  // 204.43f, 279.834f, 197.108f, 287.156f
 path.quadTo(SkBits2Float(0x433dc912), SkBits2Float(0x43933d38), SkBits2Float(0x43336e1b), SkBits2Float(0x43933d38));  // 189.785f, 294.478f, 179.43f, 294.478f
@@ -553,7 +549,7 @@
     builder.add(path35, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b08773));  // 292.838f, 353.058f
 path.quadTo(SkBits2Float(0x43926b36), SkBits2Float(0x43b5b4ef), SkBits2Float(0x438ec1f4), SkBits2Float(0x43b95e31));  // 292.838f, 363.414f, 285.515f, 370.736f
 path.quadTo(SkBits2Float(0x438b18b2), SkBits2Float(0x43bd0773), SkBits2Float(0x4385eb36), SkBits2Float(0x43bd0773));  // 278.193f, 378.058f, 267.838f, 378.058f
@@ -568,7 +564,7 @@
     builder.add(path36, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ea874d), SkBits2Float(0x4382542c));  // 469.057f, 260.658f
 path.quadTo(SkBits2Float(0x43ea874d), SkBits2Float(0x438781a8), SkBits2Float(0x43e6de0b), SkBits2Float(0x438b2aea));  // 469.057f, 271.013f, 461.735f, 278.335f
 path.quadTo(SkBits2Float(0x43e334c9), SkBits2Float(0x438ed42c), SkBits2Float(0x43de074d), SkBits2Float(0x438ed42c));  // 454.412f, 285.658f, 444.057f, 285.658f
@@ -583,7 +579,7 @@
     builder.add(path37, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42e7d715), SkBits2Float(0x436cecfc));  // 115.92f, 236.926f
 path.quadTo(SkBits2Float(0x42e7d715), SkBits2Float(0x437747f3), SkBits2Float(0x42d9320c), SkBits2Float(0x437e9a77));  // 115.92f, 247.281f, 108.598f, 254.603f
 path.quadTo(SkBits2Float(0x42ca8d04), SkBits2Float(0x4382f67e), SkBits2Float(0x42b5d715), SkBits2Float(0x4382f67e));  // 101.275f, 261.926f, 90.9201f, 261.926f
@@ -598,7 +594,7 @@
     builder.add(path38, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43545413), SkBits2Float(0x4333cfcf));  // 212.328f, 179.812f
 path.quadTo(SkBits2Float(0x43545413), SkBits2Float(0x433e2ac6), SkBits2Float(0x434d018f), SkBits2Float(0x43457d4b));  // 212.328f, 190.167f, 205.006f, 197.489f
 path.quadTo(SkBits2Float(0x4345af0a), SkBits2Float(0x434ccfcf), SkBits2Float(0x433b5413), SkBits2Float(0x434ccfcf));  // 197.684f, 204.812f, 187.328f, 204.812f
@@ -613,7 +609,7 @@
     builder.add(path39, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41823be6));  // 403.386f, 16.2792f
 path.quadTo(SkBits2Float(0x43c9b16c), SkBits2Float(0x41d513a2), SkBits2Float(0x43c6082a), SkBits2Float(0x4207d3e2));  // 403.386f, 26.6346f, 396.064f, 33.9569f
 path.quadTo(SkBits2Float(0x43c25ee8), SkBits2Float(0x42251df3), SkBits2Float(0x43bd316c), SkBits2Float(0x42251df3));  // 388.741f, 41.2792f, 378.386f, 41.2792f
@@ -628,7 +624,7 @@
     builder.add(path40, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43805e9d));  // 244.499f, 256.739f
 path.quadTo(SkBits2Float(0x43747fcb), SkBits2Float(0x43858c19), SkBits2Float(0x436d2d47), SkBits2Float(0x4389355b));  // 244.499f, 267.095f, 237.177f, 274.417f
 path.quadTo(SkBits2Float(0x4365dac2), SkBits2Float(0x438cde9d), SkBits2Float(0x435b7fcb), SkBits2Float(0x438cde9d));  // 229.855f, 281.739f, 219.499f, 281.739f
@@ -643,7 +639,7 @@
     builder.add(path41, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43910318), SkBits2Float(0x43826a1e));  // 290.024f, 260.829f
 path.quadTo(SkBits2Float(0x43910318), SkBits2Float(0x4387979a), SkBits2Float(0x438d59d6), SkBits2Float(0x438b40dc));  // 290.024f, 271.184f, 282.702f, 278.507f
 path.quadTo(SkBits2Float(0x4389b094), SkBits2Float(0x438eea1e), SkBits2Float(0x43848318), SkBits2Float(0x438eea1e));  // 275.38f, 285.829f, 265.024f, 285.829f
@@ -658,7 +654,7 @@
     builder.add(path42, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41c80000), SkBits2Float(0x436edb04));  // 25, 238.856f
 path.quadTo(SkBits2Float(0x41c80000), SkBits2Float(0x437935fb), SkBits2Float(0x418d6bde), SkBits2Float(0x43804440));  // 25, 249.211f, 17.6777f, 256.533f
 path.quadTo(SkBits2Float(0x4125af78), SkBits2Float(0x4383ed82), SkBits2Float(0x00000000), SkBits2Float(0x4383ed82));  // 10.3553f, 263.856f, 0, 263.856f
@@ -673,7 +669,7 @@
     builder.add(path43, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435d07bd), SkBits2Float(0x4395fbb5));  // 221.03f, 299.966f
 path.quadTo(SkBits2Float(0x435d07bd), SkBits2Float(0x439b2931), SkBits2Float(0x4355b539), SkBits2Float(0x439ed273));  // 221.03f, 310.322f, 213.708f, 317.644f
 path.quadTo(SkBits2Float(0x434e62b4), SkBits2Float(0x43a27bb5), SkBits2Float(0x434407bd), SkBits2Float(0x43a27bb5));  // 206.386f, 324.966f, 196.03f, 324.966f
@@ -688,7 +684,7 @@
     builder.add(path44, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x434ac5a3));  // 327.845f, 202.772f
 path.quadTo(SkBits2Float(0x43a3ec29), SkBits2Float(0x4355209a), SkBits2Float(0x43a042e7), SkBits2Float(0x435c731f));  // 327.845f, 213.127f, 320.523f, 220.45f
 path.quadTo(SkBits2Float(0x439c99a5), SkBits2Float(0x4363c5a3), SkBits2Float(0x43976c29), SkBits2Float(0x4363c5a3));  // 313.2f, 227.772f, 302.845f, 227.772f
@@ -703,7 +699,7 @@
     builder.add(path45, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4380585e), SkBits2Float(0x43199f0e));  // 256.69f, 153.621f
 path.quadTo(SkBits2Float(0x4380585e), SkBits2Float(0x4323fa06), SkBits2Float(0x43795e38), SkBits2Float(0x432b4c8a));  // 256.69f, 163.977f, 249.368f, 171.299f
 path.quadTo(SkBits2Float(0x43720bb4), SkBits2Float(0x43329f0e), SkBits2Float(0x4367b0bc), SkBits2Float(0x43329f0e));  // 242.046f, 178.621f, 231.69f, 178.621f
@@ -718,7 +714,7 @@
     builder.add(path46, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43c5e452));  // 217.618f, 395.784f
 path.quadTo(SkBits2Float(0x43599e4b), SkBits2Float(0x43cb11ce), SkBits2Float(0x43524bc7), SkBits2Float(0x43cebb10));  // 217.618f, 406.139f, 210.296f, 413.461f
 path.quadTo(SkBits2Float(0x434af942), SkBits2Float(0x43d26452), SkBits2Float(0x43409e4b), SkBits2Float(0x43d26452));  // 202.974f, 420.784f, 192.618f, 420.784f
@@ -733,7 +729,7 @@
     builder.add(path47, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a32a69));  // 222.16f, 326.331f
 path.quadTo(SkBits2Float(0x435e28dc), SkBits2Float(0x43a857e5), SkBits2Float(0x4356d658), SkBits2Float(0x43ac0127));  // 222.16f, 336.687f, 214.837f, 344.009f
 path.quadTo(SkBits2Float(0x434f83d4), SkBits2Float(0x43afaa69), SkBits2Float(0x434528dc), SkBits2Float(0x43afaa69));  // 207.515f, 351.331f, 197.16f, 351.331f
@@ -748,7 +744,7 @@
     builder.add(path48, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x438034ac));  // 187.312f, 256.411f
 path.quadTo(SkBits2Float(0x433b4ff9), SkBits2Float(0x43856228), SkBits2Float(0x4333fd75), SkBits2Float(0x43890b6a));  // 187.312f, 266.767f, 179.99f, 274.089f
 path.quadTo(SkBits2Float(0x432caaf0), SkBits2Float(0x438cb4ac), SkBits2Float(0x43224ff9), SkBits2Float(0x438cb4ac));  // 172.668f, 281.411f, 162.312f, 281.411f
@@ -763,7 +759,7 @@
     builder.add(path49, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x435b284b));  // 198.761f, 219.157f
 path.quadTo(SkBits2Float(0x4346c2ee), SkBits2Float(0x43658342), SkBits2Float(0x433f706a), SkBits2Float(0x436cd5c7));  // 198.761f, 229.513f, 191.439f, 236.835f
 path.quadTo(SkBits2Float(0x43381de6), SkBits2Float(0x4374284b), SkBits2Float(0x432dc2ee), SkBits2Float(0x4374284b));  // 184.117f, 244.157f, 173.761f, 244.157f
@@ -778,7 +774,7 @@
     builder.add(path50, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438812a5));  // 502.101f, 272.146f
 path.quadTo(SkBits2Float(0x43fb0cf6), SkBits2Float(0x438d4021), SkBits2Float(0x43f763b4), SkBits2Float(0x4390e963));  // 502.101f, 282.501f, 494.779f, 289.823f
 path.quadTo(SkBits2Float(0x43f3ba72), SkBits2Float(0x439492a5), SkBits2Float(0x43ee8cf6), SkBits2Float(0x439492a5));  // 487.457f, 297.146f, 477.101f, 297.146f
@@ -793,7 +789,7 @@
     builder.add(path51, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x438f42d6), SkBits2Float(0x435a09d4));  // 286.522f, 218.038f
 path.quadTo(SkBits2Float(0x438f42d6), SkBits2Float(0x436464cc), SkBits2Float(0x438b9994), SkBits2Float(0x436bb750));  // 286.522f, 228.394f, 279.2f, 235.716f
 path.quadTo(SkBits2Float(0x4387f052), SkBits2Float(0x437309d4), SkBits2Float(0x4382c2d6), SkBits2Float(0x437309d4));  // 271.878f, 243.038f, 261.522f, 243.038f
@@ -808,7 +804,7 @@
     builder.add(path52, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x43378440));  // 344.195f, 183.517f
 path.quadTo(SkBits2Float(0x43ac18fb), SkBits2Float(0x4341df38), SkBits2Float(0x43a86fb9), SkBits2Float(0x434931bc));  // 344.195f, 193.872f, 336.873f, 201.194f
 path.quadTo(SkBits2Float(0x43a4c677), SkBits2Float(0x43508440), SkBits2Float(0x439f98fb), SkBits2Float(0x43508440));  // 329.551f, 208.517f, 319.195f, 208.517f
@@ -823,7 +819,7 @@
     builder.add(path53, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x430c5faa));  // 119.536f, 140.374f
 path.quadTo(SkBits2Float(0x42ef12a3), SkBits2Float(0x4316baa2), SkBits2Float(0x42e06d9a), SkBits2Float(0x431e0d26));  // 119.536f, 150.729f, 112.214f, 158.051f
 path.quadTo(SkBits2Float(0x42d1c892), SkBits2Float(0x43255faa), SkBits2Float(0x42bd12a3), SkBits2Float(0x43255faa));  // 104.892f, 165.374f, 94.5364f, 165.374f
@@ -838,7 +834,7 @@
     builder.add(path54, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43463314));  // 490.826f, 198.2f
 path.quadTo(SkBits2Float(0x43f569c1), SkBits2Float(0x43508e0c), SkBits2Float(0x43f1c07f), SkBits2Float(0x4357e090));  // 490.826f, 208.555f, 483.504f, 215.877f
 path.quadTo(SkBits2Float(0x43ee173d), SkBits2Float(0x435f3314), SkBits2Float(0x43e8e9c1), SkBits2Float(0x435f3314));  // 476.182f, 223.2f, 465.826f, 223.2f
@@ -853,7 +849,7 @@
     builder.add(path55, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4346ee50), SkBits2Float(0x4386bdd6));  // 198.931f, 269.483f
 path.quadTo(SkBits2Float(0x4346ee50), SkBits2Float(0x438beb52), SkBits2Float(0x433f9bcc), SkBits2Float(0x438f9494));  // 198.931f, 279.838f, 191.609f, 287.161f
 path.quadTo(SkBits2Float(0x43384948), SkBits2Float(0x43933dd6), SkBits2Float(0x432dee50), SkBits2Float(0x43933dd6));  // 184.286f, 294.483f, 173.931f, 294.483f
@@ -868,7 +864,7 @@
     builder.add(path56, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438666a7));  // 526.959f, 268.802f
 path.quadTo(SkBits2Float(0x4403bd60), SkBits2Float(0x438b9423), SkBits2Float(0x4401e8bf), SkBits2Float(0x438f3d65));  // 526.959f, 279.157f, 519.637f, 286.48f
 path.quadTo(SkBits2Float(0x4400141e), SkBits2Float(0x4392e6a7), SkBits2Float(0x43fafac0), SkBits2Float(0x4392e6a7));  // 512.314f, 293.802f, 501.959f, 293.802f
@@ -883,7 +879,7 @@
     builder.add(path57, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x433c6aff), SkBits2Float(0x439bf9f9));  // 188.418f, 311.953f
 path.quadTo(SkBits2Float(0x433c6aff), SkBits2Float(0x43a12775), SkBits2Float(0x4335187b), SkBits2Float(0x43a4d0b7));  // 188.418f, 322.308f, 181.096f, 329.631f
 path.quadTo(SkBits2Float(0x432dc5f6), SkBits2Float(0x43a879f9), SkBits2Float(0x43236aff), SkBits2Float(0x43a879f9));  // 173.773f, 336.953f, 163.418f, 336.953f
@@ -898,7 +894,7 @@
     builder.add(path58, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x43598b8a));  // 303.414f, 217.545f
 path.quadTo(SkBits2Float(0x4397b4f8), SkBits2Float(0x4363e682), SkBits2Float(0x43940bb6), SkBits2Float(0x436b3906));  // 303.414f, 227.9f, 296.091f, 235.223f
 path.quadTo(SkBits2Float(0x43906274), SkBits2Float(0x43728b8a), SkBits2Float(0x438b34f8), SkBits2Float(0x43728b8a));  // 288.769f, 242.545f, 278.414f, 242.545f
@@ -913,7 +909,7 @@
     builder.add(path59, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x435ebbfb));  // 141.485f, 222.734f
 path.quadTo(SkBits2Float(0x430d7c0c), SkBits2Float(0x436916f2), SkBits2Float(0x43062988), SkBits2Float(0x43706977));  // 141.485f, 233.09f, 134.162f, 240.412f
 path.quadTo(SkBits2Float(0x42fdae07), SkBits2Float(0x4377bbfb), SkBits2Float(0x42e8f818), SkBits2Float(0x4377bbfb));  // 126.84f, 247.734f, 116.485f, 247.734f
@@ -928,7 +924,7 @@
     builder.add(path60, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42e664c0));  // 366.377f, 115.197f
 path.quadTo(SkBits2Float(0x43b7303b), SkBits2Float(0x42fb1aaf), SkBits2Float(0x43b386f9), SkBits2Float(0x4304dfdc));  // 366.377f, 125.552f, 359.054f, 132.874f
 path.quadTo(SkBits2Float(0x43afddb7), SkBits2Float(0x430c3260), SkBits2Float(0x43aab03b), SkBits2Float(0x430c3260));  // 351.732f, 140.197f, 341.377f, 140.197f
@@ -943,7 +939,7 @@
     builder.add(path61, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ded748), SkBits2Float(0x43786398));  // 445.682f, 248.389f
 path.quadTo(SkBits2Float(0x43ded748), SkBits2Float(0x43815f48), SkBits2Float(0x43db2e06), SkBits2Float(0x4385088a));  // 445.682f, 258.744f, 438.36f, 266.067f
 path.quadTo(SkBits2Float(0x43d784c4), SkBits2Float(0x4388b1cc), SkBits2Float(0x43d25748), SkBits2Float(0x4388b1cc));  // 431.037f, 273.389f, 420.682f, 273.389f
@@ -958,7 +954,7 @@
     builder.add(path62, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x4397997d));  // 375.877f, 303.199f
 path.quadTo(SkBits2Float(0x43bbf04d), SkBits2Float(0x439cc6f9), SkBits2Float(0x43b8470b), SkBits2Float(0x43a0703b));  // 375.877f, 313.554f, 368.555f, 320.877f
 path.quadTo(SkBits2Float(0x43b49dc9), SkBits2Float(0x43a4197d), SkBits2Float(0x43af704d), SkBits2Float(0x43a4197d));  // 361.233f, 328.199f, 350.877f, 328.199f
@@ -973,7 +969,7 @@
     builder.add(path63, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b62e97), SkBits2Float(0x4313ec95));  // 364.364f, 147.924f
 path.quadTo(SkBits2Float(0x43b62e97), SkBits2Float(0x431e478c), SkBits2Float(0x43b28555), SkBits2Float(0x43259a11));  // 364.364f, 158.279f, 357.042f, 165.602f
 path.quadTo(SkBits2Float(0x43aedc13), SkBits2Float(0x432cec95), SkBits2Float(0x43a9ae97), SkBits2Float(0x432cec95));  // 349.719f, 172.924f, 339.364f, 172.924f
@@ -988,7 +984,7 @@
     builder.add(path64, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b37791));  // 473.054f, 358.934f
 path.quadTo(SkBits2Float(0x43ec86f7), SkBits2Float(0x43b8a50d), SkBits2Float(0x43e8ddb5), SkBits2Float(0x43bc4e4f));  // 473.054f, 369.289f, 465.732f, 376.612f
 path.quadTo(SkBits2Float(0x43e53473), SkBits2Float(0x43bff791), SkBits2Float(0x43e006f7), SkBits2Float(0x43bff791));  // 458.41f, 383.934f, 448.054f, 383.934f
@@ -1003,7 +999,7 @@
     builder.add(path65, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43840826), SkBits2Float(0x43927b24));  // 264.064f, 292.962f
 path.quadTo(SkBits2Float(0x43840826), SkBits2Float(0x4397a8a0), SkBits2Float(0x43805ee4), SkBits2Float(0x439b51e2));  // 264.064f, 303.317f, 256.741f, 310.64f
 path.quadTo(SkBits2Float(0x43796b44), SkBits2Float(0x439efb24), SkBits2Float(0x436f104c), SkBits2Float(0x439efb24));  // 249.419f, 317.962f, 239.064f, 317.962f
@@ -1018,7 +1014,7 @@
     builder.add(path66, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4387c794), SkBits2Float(0x43567a78));  // 271.559f, 214.478f
 path.quadTo(SkBits2Float(0x4387c794), SkBits2Float(0x4360d570), SkBits2Float(0x43841e52), SkBits2Float(0x436827f4));  // 271.559f, 224.834f, 264.237f, 232.156f
 path.quadTo(SkBits2Float(0x43807510), SkBits2Float(0x436f7a78), SkBits2Float(0x43768f28), SkBits2Float(0x436f7a78));  // 256.915f, 239.478f, 246.559f, 239.478f
@@ -1033,7 +1029,7 @@
     builder.add(path67, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x4314ea3c));  // 415.556f, 148.915f
 path.quadTo(SkBits2Float(0x43cfc71f), SkBits2Float(0x431f4534), SkBits2Float(0x43cc1ddd), SkBits2Float(0x432697b8));  // 415.556f, 159.27f, 408.233f, 166.593f
 path.quadTo(SkBits2Float(0x43c8749b), SkBits2Float(0x432dea3c), SkBits2Float(0x43c3471f), SkBits2Float(0x432dea3c));  // 400.911f, 173.915f, 390.556f, 173.915f
@@ -1048,7 +1044,7 @@
     builder.add(path68, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a48336), SkBits2Float(0x4336f503));  // 329.025f, 182.957f
 path.quadTo(SkBits2Float(0x43a48336), SkBits2Float(0x43414ffa), SkBits2Float(0x43a0d9f4), SkBits2Float(0x4348a27f));  // 329.025f, 193.312f, 321.703f, 200.635f
 path.quadTo(SkBits2Float(0x439d30b2), SkBits2Float(0x434ff503), SkBits2Float(0x43980336), SkBits2Float(0x434ff503));  // 314.38f, 207.957f, 304.025f, 207.957f
@@ -1063,7 +1059,7 @@
     builder.add(path69, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439a774e), SkBits2Float(0x43a57837));  // 308.932f, 330.939f
 path.quadTo(SkBits2Float(0x439a774e), SkBits2Float(0x43aaa5b3), SkBits2Float(0x4396ce0c), SkBits2Float(0x43ae4ef5));  // 308.932f, 341.295f, 301.61f, 348.617f
 path.quadTo(SkBits2Float(0x439324ca), SkBits2Float(0x43b1f837), SkBits2Float(0x438df74e), SkBits2Float(0x43b1f837));  // 294.287f, 355.939f, 283.932f, 355.939f
@@ -1078,7 +1074,7 @@
     builder.add(path70, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439be624), SkBits2Float(0x438cec6f));  // 311.798f, 281.847f
 path.quadTo(SkBits2Float(0x439be624), SkBits2Float(0x439219eb), SkBits2Float(0x43983ce2), SkBits2Float(0x4395c32d));  // 311.798f, 292.202f, 304.476f, 299.525f
 path.quadTo(SkBits2Float(0x439493a0), SkBits2Float(0x43996c6f), SkBits2Float(0x438f6624), SkBits2Float(0x43996c6f));  // 297.153f, 306.847f, 286.798f, 306.847f
@@ -1093,7 +1089,7 @@
     builder.add(path71, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43714851), SkBits2Float(0x43aff2c0));  // 241.282f, 351.896f
 path.quadTo(SkBits2Float(0x43714851), SkBits2Float(0x43b5203c), SkBits2Float(0x4369f5cd), SkBits2Float(0x43b8c97e));  // 241.282f, 362.252f, 233.96f, 369.574f
 path.quadTo(SkBits2Float(0x4362a348), SkBits2Float(0x43bc72c0), SkBits2Float(0x43584851), SkBits2Float(0x43bc72c0));  // 226.638f, 376.896f, 216.282f, 376.896f
@@ -1108,7 +1104,7 @@
     builder.add(path72, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43e644d3), SkBits2Float(0x43041b70));  // 460.538f, 132.107f
 path.quadTo(SkBits2Float(0x43e644d3), SkBits2Float(0x430e7668), SkBits2Float(0x43e29b91), SkBits2Float(0x4315c8ec));  // 460.538f, 142.463f, 453.215f, 149.785f
 path.quadTo(SkBits2Float(0x43def24f), SkBits2Float(0x431d1b70), SkBits2Float(0x43d9c4d3), SkBits2Float(0x431d1b70));  // 445.893f, 157.107f, 435.538f, 157.107f
@@ -1123,7 +1119,7 @@
     builder.add(path73, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43c29be7), SkBits2Float(0x4366bcd5));  // 389.218f, 230.738f
 path.quadTo(SkBits2Float(0x43c29be7), SkBits2Float(0x437117cc), SkBits2Float(0x43bef2a5), SkBits2Float(0x43786a51));  // 389.218f, 241.093f, 381.896f, 248.415f
 path.quadTo(SkBits2Float(0x43bb4963), SkBits2Float(0x437fbcd5), SkBits2Float(0x43b61be7), SkBits2Float(0x437fbcd5));  // 374.573f, 255.738f, 364.218f, 255.738f
@@ -1138,7 +1134,7 @@
     builder.add(path74, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b8637d), SkBits2Float(0x435f209d));  // 368.777f, 223.127f
 path.quadTo(SkBits2Float(0x43b8637d), SkBits2Float(0x43697b94), SkBits2Float(0x43b4ba3b), SkBits2Float(0x4370ce19));  // 368.777f, 233.483f, 361.455f, 240.805f
 path.quadTo(SkBits2Float(0x43b110f9), SkBits2Float(0x4378209d), SkBits2Float(0x43abe37d), SkBits2Float(0x4378209d));  // 354.133f, 248.127f, 343.777f, 248.127f
@@ -1153,7 +1149,7 @@
     builder.add(path75, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x435dba35));  // 327.848f, 221.727f
 path.quadTo(SkBits2Float(0x43a3ec8f), SkBits2Float(0x4368152c), SkBits2Float(0x43a0434d), SkBits2Float(0x436f67b1));  // 327.848f, 232.083f, 320.526f, 239.405f
 path.quadTo(SkBits2Float(0x439c9a0b), SkBits2Float(0x4376ba35), SkBits2Float(0x43976c8f), SkBits2Float(0x4376ba35));  // 313.203f, 246.727f, 302.848f, 246.727f
@@ -1168,7 +1164,7 @@
     builder.add(path76, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4408ede5), SkBits2Float(0x436af388));  // 547.717f, 234.951f
 path.quadTo(SkBits2Float(0x4408ede5), SkBits2Float(0x43754e7f), SkBits2Float(0x44071944), SkBits2Float(0x437ca103));  // 547.717f, 245.307f, 540.395f, 252.629f
 path.quadTo(SkBits2Float(0x440544a3), SkBits2Float(0x4381f9c4), SkBits2Float(0x4402ade5), SkBits2Float(0x4381f9c4));  // 533.072f, 259.951f, 522.717f, 259.951f
@@ -1183,7 +1179,7 @@
     builder.add(path77, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a06718), SkBits2Float(0x43848d47));  // 320.805f, 265.104f
 path.quadTo(SkBits2Float(0x43a06718), SkBits2Float(0x4389bac3), SkBits2Float(0x439cbdd6), SkBits2Float(0x438d6405));  // 320.805f, 275.459f, 313.483f, 282.781f
 path.quadTo(SkBits2Float(0x43991494), SkBits2Float(0x43910d47), SkBits2Float(0x4393e718), SkBits2Float(0x43910d47));  // 306.161f, 290.104f, 295.805f, 290.104f
@@ -1198,7 +1194,7 @@
     builder.add(path78, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a4f721), SkBits2Float(0x439dea2f));  // 329.931f, 315.83f
 path.quadTo(SkBits2Float(0x43a4f721), SkBits2Float(0x43a317ab), SkBits2Float(0x43a14ddf), SkBits2Float(0x43a6c0ed));  // 329.931f, 326.185f, 322.608f, 333.507f
 path.quadTo(SkBits2Float(0x439da49d), SkBits2Float(0x43aa6a2f), SkBits2Float(0x43987721), SkBits2Float(0x43aa6a2f));  // 315.286f, 340.83f, 304.931f, 340.83f
@@ -1213,7 +1209,7 @@
     builder.add(path79, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x434a2b20));  // 195.956f, 202.168f
 path.quadTo(SkBits2Float(0x4343f4a9), SkBits2Float(0x43548618), SkBits2Float(0x433ca225), SkBits2Float(0x435bd89c));  // 195.956f, 212.524f, 188.633f, 219.846f
 path.quadTo(SkBits2Float(0x43354fa0), SkBits2Float(0x43632b20), SkBits2Float(0x432af4a9), SkBits2Float(0x43632b20));  // 181.311f, 227.168f, 170.956f, 227.168f
@@ -1228,7 +1224,7 @@
     builder.add(path80, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4281a60e), SkBits2Float(0x42f78d6e));  // 64.8243f, 123.776f
 path.quadTo(SkBits2Float(0x4281a60e), SkBits2Float(0x430621ae), SkBits2Float(0x4266020c), SkBits2Float(0x430d7433));  // 64.8243f, 134.132f, 57.502f, 141.454f
 path.quadTo(SkBits2Float(0x4248b7fa), SkBits2Float(0x4314c6b7), SkBits2Float(0x421f4c1c), SkBits2Float(0x4314c6b7));  // 50.1797f, 148.776f, 39.8243f, 148.776f
@@ -1243,7 +1239,7 @@
     builder.add(path81, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43740113), SkBits2Float(0x4363dcf0));  // 244.004f, 227.863f
 path.quadTo(SkBits2Float(0x43740113), SkBits2Float(0x436e37e8), SkBits2Float(0x436cae8f), SkBits2Float(0x43758a6c));  // 244.004f, 238.218f, 236.682f, 245.541f
 path.quadTo(SkBits2Float(0x43655c0a), SkBits2Float(0x437cdcf0), SkBits2Float(0x435b0113), SkBits2Float(0x437cdcf0));  // 229.36f, 252.863f, 219.004f, 252.863f
@@ -1258,7 +1254,7 @@
     builder.add(path82, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43bf0464), SkBits2Float(0x431e17ca));  // 382.034f, 158.093f
 path.quadTo(SkBits2Float(0x43bf0464), SkBits2Float(0x432872c2), SkBits2Float(0x43bb5b22), SkBits2Float(0x432fc546));  // 382.034f, 168.448f, 374.712f, 175.771f
 path.quadTo(SkBits2Float(0x43b7b1e0), SkBits2Float(0x433717ca), SkBits2Float(0x43b28464), SkBits2Float(0x433717ca));  // 367.39f, 183.093f, 357.034f, 183.093f
@@ -1273,7 +1269,7 @@
     builder.add(path83, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a72678), SkBits2Float(0x438273e0));  // 334.301f, 260.905f
 path.quadTo(SkBits2Float(0x43a72678), SkBits2Float(0x4387a15c), SkBits2Float(0x43a37d36), SkBits2Float(0x438b4a9e));  // 334.301f, 271.261f, 326.978f, 278.583f
 path.quadTo(SkBits2Float(0x439fd3f4), SkBits2Float(0x438ef3e0), SkBits2Float(0x439aa678), SkBits2Float(0x438ef3e0));  // 319.656f, 285.905f, 309.301f, 285.905f
@@ -1288,7 +1284,7 @@
     builder.add(path84, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43464372));  // 225.746f, 198.263f
 path.quadTo(SkBits2Float(0x4361bf09), SkBits2Float(0x43509e6a), SkBits2Float(0x435a6c85), SkBits2Float(0x4357f0ee));  // 225.746f, 208.619f, 218.424f, 215.941f
 path.quadTo(SkBits2Float(0x43531a00), SkBits2Float(0x435f4372), SkBits2Float(0x4348bf09), SkBits2Float(0x435f4372));  // 211.102f, 223.263f, 200.746f, 223.263f
@@ -1303,7 +1299,7 @@
     builder.add(path85, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4377220b), SkBits2Float(0x4392653c));  // 247.133f, 292.791f
 path.quadTo(SkBits2Float(0x4377220b), SkBits2Float(0x439792b8), SkBits2Float(0x436fcf87), SkBits2Float(0x439b3bfa));  // 247.133f, 303.146f, 239.811f, 310.469f
 path.quadTo(SkBits2Float(0x43687d02), SkBits2Float(0x439ee53c), SkBits2Float(0x435e220b), SkBits2Float(0x439ee53c));  // 232.488f, 317.791f, 222.133f, 317.791f
@@ -1318,7 +1314,7 @@
     builder.add(path86, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4402a250), SkBits2Float(0x4331ae72));  // 522.536f, 177.681f
 path.quadTo(SkBits2Float(0x4402a250), SkBits2Float(0x433c096a), SkBits2Float(0x4400cdaf), SkBits2Float(0x43435bee));  // 522.536f, 188.037f, 515.214f, 195.359f
 path.quadTo(SkBits2Float(0x43fdf21c), SkBits2Float(0x434aae72), SkBits2Float(0x43f8c4a0), SkBits2Float(0x434aae72));  // 507.891f, 202.681f, 497.536f, 202.681f
@@ -1333,7 +1329,7 @@
     builder.add(path87, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439cfd65), SkBits2Float(0x4331c20a));  // 313.98f, 177.758f
 path.quadTo(SkBits2Float(0x439cfd65), SkBits2Float(0x433c1d02), SkBits2Float(0x43995423), SkBits2Float(0x43436f86));  // 313.98f, 188.113f, 306.657f, 195.436f
 path.quadTo(SkBits2Float(0x4395aae1), SkBits2Float(0x434ac20a), SkBits2Float(0x43907d65), SkBits2Float(0x434ac20a));  // 299.335f, 202.758f, 288.98f, 202.758f
@@ -1348,7 +1344,7 @@
     builder.add(path88, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x430c1343));  // 397.594f, 140.075f
 path.quadTo(SkBits2Float(0x43c6cc0f), SkBits2Float(0x43166e3a), SkBits2Float(0x43c322cd), SkBits2Float(0x431dc0bf));  // 397.594f, 150.431f, 390.272f, 157.753f
 path.quadTo(SkBits2Float(0x43bf798b), SkBits2Float(0x43251343), SkBits2Float(0x43ba4c0f), SkBits2Float(0x43251343));  // 382.95f, 165.075f, 372.594f, 165.075f
@@ -1363,7 +1359,7 @@
     builder.add(path89, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43453a6b), SkBits2Float(0x438ed8e3));  // 197.228f, 285.694f
 path.quadTo(SkBits2Float(0x43453a6b), SkBits2Float(0x4394065f), SkBits2Float(0x433de7e7), SkBits2Float(0x4397afa1));  // 197.228f, 296.05f, 189.906f, 303.372f
 path.quadTo(SkBits2Float(0x43369562), SkBits2Float(0x439b58e3), SkBits2Float(0x432c3a6b), SkBits2Float(0x439b58e3));  // 182.584f, 310.694f, 172.228f, 310.694f
@@ -1378,7 +1374,7 @@
     builder.add(path90, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a452ad), SkBits2Float(0x434c38a8));  // 328.646f, 204.221f
 path.quadTo(SkBits2Float(0x43a452ad), SkBits2Float(0x435693a0), SkBits2Float(0x43a0a96b), SkBits2Float(0x435de624));  // 328.646f, 214.577f, 321.324f, 221.899f
 path.quadTo(SkBits2Float(0x439d0029), SkBits2Float(0x436538a8), SkBits2Float(0x4397d2ad), SkBits2Float(0x436538a8));  // 314.001f, 229.221f, 303.646f, 229.221f
@@ -1393,7 +1389,7 @@
     builder.add(path91, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x44002d54), SkBits2Float(0x4339e9e8));  // 512.708f, 185.914f
 path.quadTo(SkBits2Float(0x44002d54), SkBits2Float(0x434444e0), SkBits2Float(0x43fcb166), SkBits2Float(0x434b9764));  // 512.708f, 196.269f, 505.386f, 203.591f
 path.quadTo(SkBits2Float(0x43f90824), SkBits2Float(0x4352e9e8), SkBits2Float(0x43f3daa8), SkBits2Float(0x4352e9e8));  // 498.064f, 210.914f, 487.708f, 210.914f
@@ -1408,7 +1404,7 @@
     builder.add(path92, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4385d99d), SkBits2Float(0x4302a8cd));  // 267.7f, 130.659f
 path.quadTo(SkBits2Float(0x4385d99d), SkBits2Float(0x430d03c4), SkBits2Float(0x4382305b), SkBits2Float(0x43145649));  // 267.7f, 141.015f, 260.378f, 148.337f
 path.quadTo(SkBits2Float(0x437d0e32), SkBits2Float(0x431ba8cd), SkBits2Float(0x4372b33a), SkBits2Float(0x431ba8cd));  // 253.055f, 155.659f, 242.7f, 155.659f
@@ -1423,7 +1419,7 @@
     builder.add(path93, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42f92820), SkBits2Float(0x435b1722));  // 124.578f, 219.09f
 path.quadTo(SkBits2Float(0x42f92820), SkBits2Float(0x4365721a), SkBits2Float(0x42ea8318), SkBits2Float(0x436cc49e));  // 124.578f, 229.446f, 117.256f, 236.768f
 path.quadTo(SkBits2Float(0x42dbde0f), SkBits2Float(0x43741722), SkBits2Float(0x42c72820), SkBits2Float(0x43741722));  // 109.934f, 244.09f, 99.5784f, 244.09f
@@ -1438,7 +1434,7 @@
     builder.add(path94, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b70d86), SkBits2Float(0x435cf621));  // 366.106f, 220.961f
 path.quadTo(SkBits2Float(0x43b70d86), SkBits2Float(0x43675118), SkBits2Float(0x43b36444), SkBits2Float(0x436ea39d));  // 366.106f, 231.317f, 358.783f, 238.639f
 path.quadTo(SkBits2Float(0x43afbb02), SkBits2Float(0x4375f621), SkBits2Float(0x43aa8d86), SkBits2Float(0x4375f621));  // 351.461f, 245.961f, 341.106f, 245.961f
@@ -1453,7 +1449,7 @@
     builder.add(path95, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x436d80e1), SkBits2Float(0x43885efa));  // 237.503f, 272.742f
 path.quadTo(SkBits2Float(0x436d80e1), SkBits2Float(0x438d8c76), SkBits2Float(0x43662e5d), SkBits2Float(0x439135b8));  // 237.503f, 283.097f, 230.181f, 290.42f
 path.quadTo(SkBits2Float(0x435edbd8), SkBits2Float(0x4394defa), SkBits2Float(0x435480e1), SkBits2Float(0x4394defa));  // 222.859f, 297.742f, 212.503f, 297.742f
@@ -1468,7 +1464,7 @@
     builder.add(path96, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43e54635), SkBits2Float(0x43794ba0));  // 458.548f, 249.295f
 path.quadTo(SkBits2Float(0x43e54635), SkBits2Float(0x4381d34c), SkBits2Float(0x43e19cf3), SkBits2Float(0x43857c8e));  // 458.548f, 259.651f, 451.226f, 266.973f
 path.quadTo(SkBits2Float(0x43ddf3b1), SkBits2Float(0x438925d0), SkBits2Float(0x43d8c635), SkBits2Float(0x438925d0));  // 443.904f, 274.295f, 433.548f, 274.295f
@@ -1483,7 +1479,7 @@
     builder.add(path97, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42596ac0), SkBits2Float(0x42f09a24));  // 54.3542f, 120.301f
 path.quadTo(SkBits2Float(0x42596ac0), SkBits2Float(0x4302a809), SkBits2Float(0x423c20af), SkBits2Float(0x4309fa8e));  // 54.3542f, 130.656f, 47.0319f, 137.979f
 path.quadTo(SkBits2Float(0x421ed69e), SkBits2Float(0x43114d12), SkBits2Float(0x41ead580), SkBits2Float(0x43114d12));  // 39.7096f, 145.301f, 29.3542f, 145.301f
@@ -1498,7 +1494,7 @@
     builder.add(path98, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438703b7));  // 342.568f, 270.029f
 path.quadTo(SkBits2Float(0x43ab48a8), SkBits2Float(0x438c3133), SkBits2Float(0x43a79f66), SkBits2Float(0x438fda75));  // 342.568f, 280.384f, 335.245f, 287.707f
 path.quadTo(SkBits2Float(0x43a3f624), SkBits2Float(0x439383b7), SkBits2Float(0x439ec8a8), SkBits2Float(0x439383b7));  // 327.923f, 295.029f, 317.568f, 295.029f
@@ -1513,7 +1509,7 @@
     builder.add(path99, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4394a1fa));  // 359.629f, 297.265f
 path.quadTo(SkBits2Float(0x43b3d091), SkBits2Float(0x4399cf76), SkBits2Float(0x43b0274f), SkBits2Float(0x439d78b8));  // 359.629f, 307.621f, 352.307f, 314.943f
 path.quadTo(SkBits2Float(0x43ac7e0d), SkBits2Float(0x43a121fa), SkBits2Float(0x43a75091), SkBits2Float(0x43a121fa));  // 344.985f, 322.265f, 334.629f, 322.265f
@@ -1528,7 +1524,7 @@
     builder.add(path100, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42a153aa), SkBits2Float(0x4387c2b8));  // 80.6634f, 271.521f
 path.quadTo(SkBits2Float(0x42a153aa), SkBits2Float(0x438cf034), SkBits2Float(0x4292aea2), SkBits2Float(0x43909976));  // 80.6634f, 281.877f, 73.3411f, 289.199f
 path.quadTo(SkBits2Float(0x42840999), SkBits2Float(0x439442b8), SkBits2Float(0x425ea754), SkBits2Float(0x439442b8));  // 66.0187f, 296.521f, 55.6634f, 296.521f
@@ -1543,7 +1539,7 @@
     builder.add(path101, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x436da8c1), SkBits2Float(0x434950e1));  // 237.659f, 201.316f
 path.quadTo(SkBits2Float(0x436da8c1), SkBits2Float(0x4353abd8), SkBits2Float(0x4366563d), SkBits2Float(0x435afe5d));  // 237.659f, 211.671f, 230.337f, 218.994f
 path.quadTo(SkBits2Float(0x435f03b8), SkBits2Float(0x436250e1), SkBits2Float(0x4354a8c1), SkBits2Float(0x436250e1));  // 223.015f, 226.316f, 212.659f, 226.316f
@@ -1558,7 +1554,7 @@
     builder.add(path102, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ba530a), SkBits2Float(0x4376baae));  // 372.649f, 246.729f
 path.quadTo(SkBits2Float(0x43ba530a), SkBits2Float(0x43808ad3), SkBits2Float(0x43b6a9c8), SkBits2Float(0x43843415));  // 372.649f, 257.085f, 365.326f, 264.407f
 path.quadTo(SkBits2Float(0x43b30086), SkBits2Float(0x4387dd57), SkBits2Float(0x43add30a), SkBits2Float(0x4387dd57));  // 358.004f, 271.729f, 347.649f, 271.729f
@@ -1573,7 +1569,7 @@
     builder.add(path103, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4399286a), SkBits2Float(0x435887a5));  // 306.316f, 216.53f
 path.quadTo(SkBits2Float(0x4399286a), SkBits2Float(0x4362e29c), SkBits2Float(0x43957f28), SkBits2Float(0x436a3521));  // 306.316f, 226.885f, 298.993f, 234.208f
 path.quadTo(SkBits2Float(0x4391d5e6), SkBits2Float(0x437187a5), SkBits2Float(0x438ca86a), SkBits2Float(0x437187a5));  // 291.671f, 241.53f, 281.316f, 241.53f
@@ -1588,7 +1584,7 @@
     builder.add(path104, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a08755), SkBits2Float(0x43120238));  // 321.057f, 146.009f
 path.quadTo(SkBits2Float(0x43a08755), SkBits2Float(0x431c5d30), SkBits2Float(0x439cde13), SkBits2Float(0x4323afb4));  // 321.057f, 156.364f, 313.735f, 163.686f
 path.quadTo(SkBits2Float(0x439934d1), SkBits2Float(0x432b0238), SkBits2Float(0x43940755), SkBits2Float(0x432b0238));  // 306.413f, 171.009f, 296.057f, 171.009f
@@ -1603,7 +1599,7 @@
     builder.add(path105, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43e31764), SkBits2Float(0x438e240b));  // 454.183f, 284.282f
 path.quadTo(SkBits2Float(0x43e31764), SkBits2Float(0x43935187), SkBits2Float(0x43df6e22), SkBits2Float(0x4396fac9));  // 454.183f, 294.637f, 446.86f, 301.959f
 path.quadTo(SkBits2Float(0x43dbc4e0), SkBits2Float(0x439aa40b), SkBits2Float(0x43d69764), SkBits2Float(0x439aa40b));  // 439.538f, 309.282f, 429.183f, 309.282f
@@ -1618,7 +1614,7 @@
     builder.add(path106, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43430483), SkBits2Float(0x4345764d));  // 195.018f, 197.462f
 path.quadTo(SkBits2Float(0x43430483), SkBits2Float(0x434fd144), SkBits2Float(0x433bb1ff), SkBits2Float(0x435723c9));  // 195.018f, 207.817f, 187.695f, 215.14f
 path.quadTo(SkBits2Float(0x43345f7a), SkBits2Float(0x435e764d), SkBits2Float(0x432a0483), SkBits2Float(0x435e764d));  // 180.373f, 222.462f, 170.018f, 222.462f
@@ -1633,7 +1629,7 @@
     builder.add(path107, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x439a55b2), SkBits2Float(0x4370a1cc));  // 308.669f, 240.632f
 path.quadTo(SkBits2Float(0x439a55b2), SkBits2Float(0x437afcc3), SkBits2Float(0x4396ac70), SkBits2Float(0x438127a4));  // 308.669f, 250.987f, 301.347f, 258.31f
 path.quadTo(SkBits2Float(0x4393032e), SkBits2Float(0x4384d0e6), SkBits2Float(0x438dd5b2), SkBits2Float(0x4384d0e6));  // 294.025f, 265.632f, 283.669f, 265.632f
@@ -1648,7 +1644,7 @@
     builder.add(path108, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x4327ee31));  // 126.469f, 167.93f
 path.quadTo(SkBits2Float(0x42fcf02d), SkBits2Float(0x43324928), SkBits2Float(0x42ee4b24), SkBits2Float(0x43399bad));  // 126.469f, 178.286f, 119.147f, 185.608f
 path.quadTo(SkBits2Float(0x42dfa61c), SkBits2Float(0x4340ee31), SkBits2Float(0x42caf02d), SkBits2Float(0x4340ee31));  // 111.824f, 192.93f, 101.469f, 192.93f
@@ -1663,7 +1659,7 @@
     builder.add(path109, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438aadd7));  // 546.55f, 277.358f
 path.quadTo(SkBits2Float(0x4408a32e), SkBits2Float(0x438fdb53), SkBits2Float(0x4406ce8d), SkBits2Float(0x43938495));  // 546.55f, 287.713f, 539.227f, 295.036f
 path.quadTo(SkBits2Float(0x4404f9ec), SkBits2Float(0x43972dd7), SkBits2Float(0x4402632e), SkBits2Float(0x43972dd7));  // 531.905f, 302.358f, 521.55f, 302.358f
@@ -1678,7 +1674,7 @@
     builder.add(path110, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ae6436), SkBits2Float(0x4399c25d));  // 348.783f, 307.518f
 path.quadTo(SkBits2Float(0x43ae6436), SkBits2Float(0x439eefd9), SkBits2Float(0x43aabaf4), SkBits2Float(0x43a2991b));  // 348.783f, 317.874f, 341.461f, 325.196f
 path.quadTo(SkBits2Float(0x43a711b2), SkBits2Float(0x43a6425d), SkBits2Float(0x43a1e436), SkBits2Float(0x43a6425d));  // 334.138f, 332.518f, 323.783f, 332.518f
@@ -1693,7 +1689,7 @@
     builder.add(path111, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x437ef735), SkBits2Float(0x43a4d766));  // 254.966f, 329.683f
 path.quadTo(SkBits2Float(0x437ef735), SkBits2Float(0x43aa04e2), SkBits2Float(0x4377a4b1), SkBits2Float(0x43adae24));  // 254.966f, 340.038f, 247.643f, 347.36f
 path.quadTo(SkBits2Float(0x4370522c), SkBits2Float(0x43b15766), SkBits2Float(0x4365f735), SkBits2Float(0x43b15766));  // 240.321f, 354.683f, 229.966f, 354.683f
@@ -1708,7 +1704,7 @@
     builder.add(path112, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x433e57b5));  // 163.695f, 190.343f
 path.quadTo(SkBits2Float(0x4323b1dd), SkBits2Float(0x4348b2ac), SkBits2Float(0x431c5f59), SkBits2Float(0x43500531));  // 163.695f, 200.698f, 156.372f, 208.02f
 path.quadTo(SkBits2Float(0x43150cd4), SkBits2Float(0x435757b5), SkBits2Float(0x430ab1dd), SkBits2Float(0x435757b5));  // 149.05f, 215.343f, 138.695f, 215.343f
@@ -1723,7 +1719,7 @@
     builder.add(path113, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x436e6f43), SkBits2Float(0x435d1aaa));  // 238.435f, 221.104f
 path.quadTo(SkBits2Float(0x436e6f43), SkBits2Float(0x436775a2), SkBits2Float(0x43671cbf), SkBits2Float(0x436ec826));  // 238.435f, 231.46f, 231.112f, 238.782f
 path.quadTo(SkBits2Float(0x435fca3a), SkBits2Float(0x43761aaa), SkBits2Float(0x43556f43), SkBits2Float(0x43761aaa));  // 223.79f, 246.104f, 213.435f, 246.104f
@@ -1738,7 +1734,7 @@
     builder.add(path114, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43699f20), SkBits2Float(0x43b74967));  // 233.622f, 366.573f
 path.quadTo(SkBits2Float(0x43699f20), SkBits2Float(0x43bc76e3), SkBits2Float(0x43624c9c), SkBits2Float(0x43c02025));  // 233.622f, 376.929f, 226.299f, 384.251f
 path.quadTo(SkBits2Float(0x435afa18), SkBits2Float(0x43c3c967), SkBits2Float(0x43509f20), SkBits2Float(0x43c3c967));  // 218.977f, 391.573f, 208.622f, 391.573f
@@ -1753,7 +1749,7 @@
     builder.add(path115, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x434919ea));  // 242.71f, 201.101f
 path.quadTo(SkBits2Float(0x4372b5ce), SkBits2Float(0x435374e2), SkBits2Float(0x436b634a), SkBits2Float(0x435ac766));  // 242.71f, 211.457f, 235.388f, 218.779f
 path.quadTo(SkBits2Float(0x436410c6), SkBits2Float(0x436219ea), SkBits2Float(0x4359b5ce), SkBits2Float(0x436219ea));  // 228.066f, 226.101f, 217.71f, 226.101f
@@ -1768,7 +1764,7 @@
     builder.add(path116, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43212033), SkBits2Float(0x433c0771));  // 161.126f, 188.029f
 path.quadTo(SkBits2Float(0x43212033), SkBits2Float(0x43466268), SkBits2Float(0x4319cdaf), SkBits2Float(0x434db4ed));  // 161.126f, 198.384f, 153.803f, 205.707f
 path.quadTo(SkBits2Float(0x43127b2a), SkBits2Float(0x43550771), SkBits2Float(0x43082033), SkBits2Float(0x43550771));  // 146.481f, 213.029f, 136.126f, 213.029f
@@ -1783,7 +1779,7 @@
     builder.add(path117, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x434d1191), SkBits2Float(0x431c5cdf));  // 205.069f, 156.363f
 path.quadTo(SkBits2Float(0x434d1191), SkBits2Float(0x4326b7d6), SkBits2Float(0x4345bf0d), SkBits2Float(0x432e0a5b));  // 205.069f, 166.718f, 197.746f, 174.04f
 path.quadTo(SkBits2Float(0x433e6c88), SkBits2Float(0x43355cdf), SkBits2Float(0x43341191), SkBits2Float(0x43355cdf));  // 190.424f, 181.363f, 180.069f, 181.363f
@@ -1798,7 +1794,7 @@
     builder.add(path118, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43ba1964));  // 494.923f, 372.198f
 path.quadTo(SkBits2Float(0x43f7761c), SkBits2Float(0x43bf46e0), SkBits2Float(0x43f3ccda), SkBits2Float(0x43c2f022));  // 494.923f, 382.554f, 487.6f, 389.876f
 path.quadTo(SkBits2Float(0x43f02398), SkBits2Float(0x43c69964), SkBits2Float(0x43eaf61c), SkBits2Float(0x43c69964));  // 480.278f, 397.198f, 469.923f, 397.198f
@@ -1813,7 +1809,7 @@
     builder.add(path119, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4382767e));  // 271.414f, 260.926f
 path.quadTo(SkBits2Float(0x4387b4f4), SkBits2Float(0x4387a3fa), SkBits2Float(0x43840bb2), SkBits2Float(0x438b4d3c));  // 271.414f, 271.281f, 264.091f, 278.603f
 path.quadTo(SkBits2Float(0x43806270), SkBits2Float(0x438ef67e), SkBits2Float(0x437669e8), SkBits2Float(0x438ef67e));  // 256.769f, 285.926f, 246.414f, 285.926f
@@ -1828,7 +1824,7 @@
     builder.add(path120, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43f176b1), SkBits2Float(0x437ef3b8));  // 482.927f, 254.952f
 path.quadTo(SkBits2Float(0x43f176b1), SkBits2Float(0x4384a758), SkBits2Float(0x43edcd6f), SkBits2Float(0x4388509a));  // 482.927f, 265.307f, 475.605f, 272.63f
 path.quadTo(SkBits2Float(0x43ea242d), SkBits2Float(0x438bf9dc), SkBits2Float(0x43e4f6b1), SkBits2Float(0x438bf9dc));  // 468.283f, 279.952f, 457.927f, 279.952f
@@ -1843,7 +1839,7 @@
     builder.add(path121, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x437bd45b), SkBits2Float(0x4361cf52));  // 251.83f, 225.81f
 path.quadTo(SkBits2Float(0x437bd45b), SkBits2Float(0x436c2a4a), SkBits2Float(0x437481d7), SkBits2Float(0x43737cce));  // 251.83f, 236.165f, 244.507f, 243.488f
 path.quadTo(SkBits2Float(0x436d2f52), SkBits2Float(0x437acf52), SkBits2Float(0x4362d45b), SkBits2Float(0x437acf52));  // 237.185f, 250.81f, 226.83f, 250.81f
@@ -1858,7 +1854,7 @@
     builder.add(path122, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d20000));  // 374.939f, 420
 path.quadTo(SkBits2Float(0x43bb783c), SkBits2Float(0x43d72d7c), SkBits2Float(0x43b7cefa), SkBits2Float(0x43dad6be));  // 374.939f, 430.355f, 367.617f, 437.678f
 path.quadTo(SkBits2Float(0x43b425b8), SkBits2Float(0x43de8000), SkBits2Float(0x43aef83c), SkBits2Float(0x43de8000));  // 360.295f, 445, 349.939f, 445
@@ -1873,7 +1869,7 @@
     builder.add(path123, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ca2436), SkBits2Float(0x435899a3));  // 404.283f, 216.6f
 path.quadTo(SkBits2Float(0x43ca2436), SkBits2Float(0x4362f49a), SkBits2Float(0x43c67af4), SkBits2Float(0x436a471f));  // 404.283f, 226.955f, 396.961f, 234.278f
 path.quadTo(SkBits2Float(0x43c2d1b2), SkBits2Float(0x437199a3), SkBits2Float(0x43bda436), SkBits2Float(0x437199a3));  // 389.638f, 241.6f, 379.283f, 241.6f
@@ -1888,7 +1884,7 @@
     builder.add(path124, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x440faed8), SkBits2Float(0x43474968));  // 574.732f, 199.287f
 path.quadTo(SkBits2Float(0x440faed8), SkBits2Float(0x4351a460), SkBits2Float(0x440dda37), SkBits2Float(0x4358f6e4));  // 574.732f, 209.642f, 567.41f, 216.964f
 path.quadTo(SkBits2Float(0x440c0596), SkBits2Float(0x43604968), SkBits2Float(0x44096ed8), SkBits2Float(0x43604968));  // 560.087f, 224.287f, 549.732f, 224.287f
@@ -1903,7 +1899,7 @@
     builder.add(path125, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x437055b3), SkBits2Float(0x438ae764));  // 240.335f, 277.808f
 path.quadTo(SkBits2Float(0x437055b3), SkBits2Float(0x439014e0), SkBits2Float(0x4369032f), SkBits2Float(0x4393be22));  // 240.335f, 288.163f, 233.012f, 295.485f
 path.quadTo(SkBits2Float(0x4361b0aa), SkBits2Float(0x43976764), SkBits2Float(0x435755b3), SkBits2Float(0x43976764));  // 225.69f, 302.808f, 215.335f, 302.808f
@@ -1918,7 +1914,7 @@
     builder.add(path126, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a201c9), SkBits2Float(0x43546e88));  // 324.014f, 212.432f
 path.quadTo(SkBits2Float(0x43a201c9), SkBits2Float(0x435ec980), SkBits2Float(0x439e5887), SkBits2Float(0x43661c04));  // 324.014f, 222.787f, 316.692f, 230.109f
 path.quadTo(SkBits2Float(0x439aaf45), SkBits2Float(0x436d6e88), SkBits2Float(0x439581c9), SkBits2Float(0x436d6e88));  // 309.369f, 237.432f, 299.014f, 237.432f
@@ -1933,7 +1929,7 @@
     builder.add(path127, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42d10265), SkBits2Float(0x434da766));  // 104.505f, 205.654f
 path.quadTo(SkBits2Float(0x42d10265), SkBits2Float(0x4358025e), SkBits2Float(0x42c25d5c), SkBits2Float(0x435f54e2));  // 104.505f, 216.009f, 97.1823f, 223.332f
 path.quadTo(SkBits2Float(0x42b3b854), SkBits2Float(0x4366a766), SkBits2Float(0x429f0265), SkBits2Float(0x4366a766));  // 89.86f, 230.654f, 79.5047f, 230.654f
@@ -1948,7 +1944,7 @@
     builder.add(path128, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432540f2));  // 301.797f, 165.254f
 path.quadTo(SkBits2Float(0x4396e5f4), SkBits2Float(0x432f9bea), SkBits2Float(0x43933cb2), SkBits2Float(0x4336ee6e));  // 301.797f, 175.609f, 294.474f, 182.931f
 path.quadTo(SkBits2Float(0x438f9370), SkBits2Float(0x433e40f2), SkBits2Float(0x438a65f4), SkBits2Float(0x433e40f2));  // 287.152f, 190.254f, 276.797f, 190.254f
@@ -1963,7 +1959,7 @@
     builder.add(path129, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43759ab0), SkBits2Float(0x43329c4b));  // 245.604f, 178.611f
 path.quadTo(SkBits2Float(0x43759ab0), SkBits2Float(0x433cf742), SkBits2Float(0x436e482c), SkBits2Float(0x434449c7));  // 245.604f, 188.966f, 238.282f, 196.288f
 path.quadTo(SkBits2Float(0x4366f5a8), SkBits2Float(0x434b9c4b), SkBits2Float(0x435c9ab0), SkBits2Float(0x434b9c4b));  // 230.96f, 203.611f, 220.604f, 203.611f
@@ -1978,7 +1974,7 @@
     builder.add(path130, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x43954ede));  // 333.78f, 298.616f
 path.quadTo(SkBits2Float(0x43a6e3cc), SkBits2Float(0x439a7c5a), SkBits2Float(0x43a33a8a), SkBits2Float(0x439e259c));  // 333.78f, 308.971f, 326.457f, 316.294f
 path.quadTo(SkBits2Float(0x439f9148), SkBits2Float(0x43a1cede), SkBits2Float(0x439a63cc), SkBits2Float(0x43a1cede));  // 319.135f, 323.616f, 308.78f, 323.616f
@@ -1993,7 +1989,7 @@
     builder.add(path131, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a02b61));  // 71.1251f, 320.339f
 path.quadTo(SkBits2Float(0x428e400e), SkBits2Float(0x43a558dd), SkBits2Float(0x427f360c), SkBits2Float(0x43a9021f));  // 71.1251f, 330.694f, 63.8028f, 338.017f
 path.quadTo(SkBits2Float(0x4261ebfa), SkBits2Float(0x43acab61), SkBits2Float(0x4238801c), SkBits2Float(0x43acab61));  // 56.4804f, 345.339f, 46.1251f, 345.339f
@@ -2008,7 +2004,7 @@
     builder.add(path132, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4323b27c), SkBits2Float(0x4379f49c));  // 163.697f, 249.956f
 path.quadTo(SkBits2Float(0x4323b27c), SkBits2Float(0x438227ca), SkBits2Float(0x431c5ff8), SkBits2Float(0x4385d10c));  // 163.697f, 260.311f, 156.375f, 267.633f
 path.quadTo(SkBits2Float(0x43150d74), SkBits2Float(0x43897a4e), SkBits2Float(0x430ab27c), SkBits2Float(0x43897a4e));  // 149.053f, 274.956f, 138.697f, 274.956f
@@ -2023,7 +2019,7 @@
     builder.add(path133, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43629af6), SkBits2Float(0x434de1f1));  // 226.605f, 205.883f
 path.quadTo(SkBits2Float(0x43629af6), SkBits2Float(0x43583ce8), SkBits2Float(0x435b4872), SkBits2Float(0x435f8f6d));  // 226.605f, 216.238f, 219.283f, 223.56f
 path.quadTo(SkBits2Float(0x4353f5ee), SkBits2Float(0x4366e1f1), SkBits2Float(0x43499af6), SkBits2Float(0x4366e1f1));  // 211.961f, 230.883f, 201.605f, 230.883f
@@ -2038,7 +2034,7 @@
     builder.add(path134, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43969933), SkBits2Float(0x00000000));  // 301.197f, 0
 path.quadTo(SkBits2Float(0x43969933), SkBits2Float(0x4125af78), SkBits2Float(0x4392eff1), SkBits2Float(0x418d6bde));  // 301.197f, 10.3553f, 293.875f, 17.6777f
 path.quadTo(SkBits2Float(0x438f46af), SkBits2Float(0x41c80000), SkBits2Float(0x438a1933), SkBits2Float(0x41c80000));  // 286.552f, 25, 276.197f, 25
@@ -2053,7 +2049,7 @@
     builder.add(path135, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43310d09), SkBits2Float(0x43ab2014));  // 177.051f, 342.251f
 path.quadTo(SkBits2Float(0x43310d09), SkBits2Float(0x43b04d90), SkBits2Float(0x4329ba85), SkBits2Float(0x43b3f6d2));  // 177.051f, 352.606f, 169.729f, 359.928f
 path.quadTo(SkBits2Float(0x43226800), SkBits2Float(0x43b7a014), SkBits2Float(0x43180d09), SkBits2Float(0x43b7a014));  // 162.406f, 367.251f, 152.051f, 367.251f
@@ -2068,7 +2064,7 @@
     builder.add(path136, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43c19844), SkBits2Float(0x43818fc9));  // 387.19f, 259.123f
 path.quadTo(SkBits2Float(0x43c19844), SkBits2Float(0x4386bd45), SkBits2Float(0x43bdef02), SkBits2Float(0x438a6687));  // 387.19f, 269.479f, 379.867f, 276.801f
 path.quadTo(SkBits2Float(0x43ba45c0), SkBits2Float(0x438e0fc9), SkBits2Float(0x43b51844), SkBits2Float(0x438e0fc9));  // 372.545f, 284.123f, 362.19f, 284.123f
@@ -2083,7 +2079,7 @@
     builder.add(path137, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42de6e20));  // 214.371f, 111.215f
 path.quadTo(SkBits2Float(0x43565ef5), SkBits2Float(0x42f3240f), SkBits2Float(0x434f0c71), SkBits2Float(0x4300e48c));  // 214.371f, 121.57f, 207.049f, 128.893f
 path.quadTo(SkBits2Float(0x4347b9ec), SkBits2Float(0x43083710), SkBits2Float(0x433d5ef5), SkBits2Float(0x43083710));  // 199.726f, 136.215f, 189.371f, 136.215f
@@ -2098,7 +2094,7 @@
     builder.add(path138, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x439f6574));  // 465.307f, 318.793f
 path.quadTo(SkBits2Float(0x43e8a73d), SkBits2Float(0x43a492f0), SkBits2Float(0x43e4fdfb), SkBits2Float(0x43a83c32));  // 465.307f, 329.148f, 457.984f, 336.47f
 path.quadTo(SkBits2Float(0x43e154b9), SkBits2Float(0x43abe574), SkBits2Float(0x43dc273d), SkBits2Float(0x43abe574));  // 450.662f, 343.793f, 440.307f, 343.793f
@@ -2113,7 +2109,7 @@
     builder.add(path139, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42d3af00));  // 343.594f, 105.842f
 path.quadTo(SkBits2Float(0x43abcc05), SkBits2Float(0x42e864ef), SkBits2Float(0x43a822c3), SkBits2Float(0x42f709f8));  // 343.594f, 116.197f, 336.272f, 123.519f
 path.quadTo(SkBits2Float(0x43a47981), SkBits2Float(0x4302d780), SkBits2Float(0x439f4c05), SkBits2Float(0x4302d780));  // 328.949f, 130.842f, 318.594f, 130.842f
@@ -2128,7 +2124,7 @@
     builder.add(path140, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x438bdd2b));  // 285.125f, 279.728f
 path.quadTo(SkBits2Float(0x438e8ffa), SkBits2Float(0x43910aa7), SkBits2Float(0x438ae6b8), SkBits2Float(0x4394b3e9));  // 285.125f, 290.083f, 277.802f, 297.406f
 path.quadTo(SkBits2Float(0x43873d76), SkBits2Float(0x43985d2b), SkBits2Float(0x43820ffa), SkBits2Float(0x43985d2b));  // 270.48f, 304.728f, 260.125f, 304.728f
@@ -2143,7 +2139,7 @@
     builder.add(path141, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x441d8000), SkBits2Float(0x435e8072));  // 630, 222.502f
 path.quadTo(SkBits2Float(0x441d8000), SkBits2Float(0x4368db6a), SkBits2Float(0x441bab5f), SkBits2Float(0x43702dee));  // 630, 232.857f, 622.678f, 240.179f
 path.quadTo(SkBits2Float(0x4419d6be), SkBits2Float(0x43778072), SkBits2Float(0x44174000), SkBits2Float(0x43778072));  // 615.355f, 247.502f, 605, 247.502f
@@ -2158,7 +2154,7 @@
     builder.add(path142, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43cab007), SkBits2Float(0x4370ffc2));  // 405.375f, 240.999f
 path.quadTo(SkBits2Float(0x43cab007), SkBits2Float(0x437b5aba), SkBits2Float(0x43c706c5), SkBits2Float(0x4381569f));  // 405.375f, 251.354f, 398.053f, 258.677f
 path.quadTo(SkBits2Float(0x43c35d83), SkBits2Float(0x4384ffe1), SkBits2Float(0x43be3007), SkBits2Float(0x4384ffe1));  // 390.731f, 265.999f, 380.375f, 265.999f
@@ -2173,7 +2169,7 @@
     builder.add(path143, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x43587ea0));  // 348.371f, 216.495f
 path.quadTo(SkBits2Float(0x43ae2f7d), SkBits2Float(0x4362d998), SkBits2Float(0x43aa863b), SkBits2Float(0x436a2c1c));  // 348.371f, 226.85f, 341.049f, 234.172f
 path.quadTo(SkBits2Float(0x43a6dcf9), SkBits2Float(0x43717ea0), SkBits2Float(0x43a1af7d), SkBits2Float(0x43717ea0));  // 333.726f, 241.495f, 323.371f, 241.495f
@@ -2188,7 +2184,7 @@
     builder.add(path144, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43add1a9), SkBits2Float(0x436897ac));  // 347.638f, 232.592f
 path.quadTo(SkBits2Float(0x43add1a9), SkBits2Float(0x4372f2a3), SkBits2Float(0x43aa2867), SkBits2Float(0x437a4527));  // 347.638f, 242.948f, 340.316f, 250.27f
 path.quadTo(SkBits2Float(0x43a67f25), SkBits2Float(0x4380cbd6), SkBits2Float(0x43a151a9), SkBits2Float(0x4380cbd6));  // 332.993f, 257.592f, 322.638f, 257.592f
@@ -2203,7 +2199,7 @@
     builder.add(path145, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x438cffd4));  // 486.972f, 281.999f
 path.quadTo(SkBits2Float(0x43f37c5c), SkBits2Float(0x43922d50), SkBits2Float(0x43efd31a), SkBits2Float(0x4395d692));  // 486.972f, 292.354f, 479.649f, 299.676f
 path.quadTo(SkBits2Float(0x43ec29d8), SkBits2Float(0x43997fd4), SkBits2Float(0x43e6fc5c), SkBits2Float(0x43997fd4));  // 472.327f, 306.999f, 461.972f, 306.999f
@@ -2218,7 +2214,7 @@
     builder.add(path146, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x434938a6), SkBits2Float(0x439b6682));  // 201.221f, 310.801f
 path.quadTo(SkBits2Float(0x434938a6), SkBits2Float(0x43a093fe), SkBits2Float(0x4341e622), SkBits2Float(0x43a43d40));  // 201.221f, 321.156f, 193.899f, 328.479f
 path.quadTo(SkBits2Float(0x433a939e), SkBits2Float(0x43a7e682), SkBits2Float(0x433038a6), SkBits2Float(0x43a7e682));  // 186.577f, 335.801f, 176.221f, 335.801f
@@ -2233,7 +2229,7 @@
     builder.add(path147, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43957ad9), SkBits2Float(0x4340d6a9));  // 298.96f, 192.839f
 path.quadTo(SkBits2Float(0x43957ad9), SkBits2Float(0x434b31a0), SkBits2Float(0x4391d197), SkBits2Float(0x43528425));  // 298.96f, 203.194f, 291.637f, 210.516f
 path.quadTo(SkBits2Float(0x438e2855), SkBits2Float(0x4359d6a9), SkBits2Float(0x4388fad9), SkBits2Float(0x4359d6a9));  // 284.315f, 217.839f, 273.96f, 217.839f
@@ -2248,7 +2244,7 @@
     builder.add(path148, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x431623e6));  // 217.804f, 150.14f
 path.quadTo(SkBits2Float(0x4359cdc3), SkBits2Float(0x43207ede), SkBits2Float(0x43527b3f), SkBits2Float(0x4327d162));  // 217.804f, 160.496f, 210.481f, 167.818f
 path.quadTo(SkBits2Float(0x434b28ba), SkBits2Float(0x432f23e6), SkBits2Float(0x4340cdc3), SkBits2Float(0x432f23e6));  // 203.159f, 175.14f, 192.804f, 175.14f
@@ -2263,7 +2259,7 @@
     builder.add(path149, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43c5524a));  // 299.681f, 394.643f
 path.quadTo(SkBits2Float(0x4395d72b), SkBits2Float(0x43ca7fc6), SkBits2Float(0x43922de9), SkBits2Float(0x43ce2908));  // 299.681f, 404.998f, 292.359f, 412.321f
 path.quadTo(SkBits2Float(0x438e84a7), SkBits2Float(0x43d1d24a), SkBits2Float(0x4389572b), SkBits2Float(0x43d1d24a));  // 285.036f, 419.643f, 274.681f, 419.643f
@@ -2278,7 +2274,7 @@
     builder.add(path150, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43884f5e), SkBits2Float(0x436c6388));  // 272.62f, 236.389f
 path.quadTo(SkBits2Float(0x43884f5e), SkBits2Float(0x4376be7f), SkBits2Float(0x4384a61c), SkBits2Float(0x437e1103));  // 272.62f, 246.744f, 265.298f, 254.066f
 path.quadTo(SkBits2Float(0x4380fcda), SkBits2Float(0x4382b1c4), SkBits2Float(0x43779ebc), SkBits2Float(0x4382b1c4));  // 257.975f, 261.389f, 247.62f, 261.389f
@@ -2293,7 +2289,7 @@
     builder.add(path151, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ac3331), SkBits2Float(0x4330c484));  // 344.4f, 176.768f
 path.quadTo(SkBits2Float(0x43ac3331), SkBits2Float(0x433b1f7c), SkBits2Float(0x43a889ef), SkBits2Float(0x43427200));  // 344.4f, 187.123f, 337.078f, 194.445f
 path.quadTo(SkBits2Float(0x43a4e0ad), SkBits2Float(0x4349c484), SkBits2Float(0x439fb331), SkBits2Float(0x4349c484));  // 329.755f, 201.768f, 319.4f, 201.768f
@@ -2308,7 +2304,7 @@
     builder.add(path152, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435659f0), SkBits2Float(0x438a1820));  // 214.351f, 276.188f
 path.quadTo(SkBits2Float(0x435659f0), SkBits2Float(0x438f459c), SkBits2Float(0x434f076c), SkBits2Float(0x4392eede));  // 214.351f, 286.544f, 207.029f, 293.866f
 path.quadTo(SkBits2Float(0x4347b4e8), SkBits2Float(0x43969820), SkBits2Float(0x433d59f0), SkBits2Float(0x43969820));  // 199.707f, 301.188f, 189.351f, 301.188f
@@ -2323,7 +2319,7 @@
     builder.add(path153, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x437aa20e), SkBits2Float(0x434e714e));  // 250.633f, 206.443f
 path.quadTo(SkBits2Float(0x437aa20e), SkBits2Float(0x4358cc46), SkBits2Float(0x43734f8a), SkBits2Float(0x43601eca));  // 250.633f, 216.798f, 243.311f, 224.12f
 path.quadTo(SkBits2Float(0x436bfd06), SkBits2Float(0x4367714e), SkBits2Float(0x4361a20e), SkBits2Float(0x4367714e));  // 235.988f, 231.443f, 225.633f, 231.443f
@@ -2338,7 +2334,7 @@
     builder.add(path154, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x439da644));  // 474.273f, 315.299f
 path.quadTo(SkBits2Float(0x43ed22e3), SkBits2Float(0x43a2d3c0), SkBits2Float(0x43e979a1), SkBits2Float(0x43a67d02));  // 474.273f, 325.654f, 466.95f, 332.977f
 path.quadTo(SkBits2Float(0x43e5d05f), SkBits2Float(0x43aa2644), SkBits2Float(0x43e0a2e3), SkBits2Float(0x43aa2644));  // 459.628f, 340.299f, 449.273f, 340.299f
@@ -2353,7 +2349,7 @@
     builder.add(path155, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43cc4428), SkBits2Float(0x43797560));  // 408.532f, 249.458f
 path.quadTo(SkBits2Float(0x43cc4428), SkBits2Float(0x4381e82c), SkBits2Float(0x43c89ae6), SkBits2Float(0x4385916e));  // 408.532f, 259.814f, 401.21f, 267.136f
 path.quadTo(SkBits2Float(0x43c4f1a4), SkBits2Float(0x43893ab0), SkBits2Float(0x43bfc428), SkBits2Float(0x43893ab0));  // 393.888f, 274.458f, 383.532f, 274.458f
@@ -2368,7 +2364,7 @@
     builder.add(path156, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43b97683));  // 114.241f, 370.926f
 path.quadTo(SkBits2Float(0x42e47b81), SkBits2Float(0x43bea3ff), SkBits2Float(0x42d5d678), SkBits2Float(0x43c24d41));  // 114.241f, 381.281f, 106.919f, 388.604f
 path.quadTo(SkBits2Float(0x42c73170), SkBits2Float(0x43c5f683), SkBits2Float(0x42b27b81), SkBits2Float(0x43c5f683));  // 99.5966f, 395.926f, 89.2412f, 395.926f
@@ -2383,7 +2379,7 @@
     builder.add(path157, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4384239e), SkBits2Float(0x42a36f30));  // 264.278f, 81.7172f
 path.quadTo(SkBits2Float(0x4384239e), SkBits2Float(0x42b8251f), SkBits2Float(0x43807a5c), SkBits2Float(0x42c6ca28));  // 264.278f, 92.0725f, 256.956f, 99.3948f
 path.quadTo(SkBits2Float(0x4379a234), SkBits2Float(0x42d56f30), SkBits2Float(0x436f473c), SkBits2Float(0x42d56f30));  // 249.634f, 106.717f, 239.278f, 106.717f
@@ -2398,7 +2394,7 @@
     builder.add(path158, (SkPathOp) 2);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x435be541), SkBits2Float(0x43baaaf6));  // 219.896f, 373.336f
 path.quadTo(SkBits2Float(0x435be541), SkBits2Float(0x43bfd872), SkBits2Float(0x435492bd), SkBits2Float(0x43c381b4));  // 219.896f, 383.691f, 212.573f, 391.013f
 path.quadTo(SkBits2Float(0x434d4038), SkBits2Float(0x43c72af6), SkBits2Float(0x4342e541), SkBits2Float(0x43c72af6));  // 205.251f, 398.336f, 194.896f, 398.336f
diff --git a/tests/PathOpsBuilderConicTest.cpp b/tests/PathOpsBuilderConicTest.cpp
index f776b43..062578c 100644
--- a/tests/PathOpsBuilderConicTest.cpp
+++ b/tests/PathOpsBuilderConicTest.cpp
@@ -54,7 +54,7 @@
 
 static void testOne(skiatest::Reporter* reporter, const OvalSet& set) {
     SkPath oval, regionResult, builderResult, opResult;
-    oval.setFillType(SkPath::kWinding_FillType);
+    oval.setFillType(SkPathFillType::kWinding);
     oval.addOval(set.fBounds);
     SkOpBuilder builder;
     SkRegion region;
@@ -412,7 +412,7 @@
 
 DEF_TEST(SixtyOvalsA, reporter) {
 SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(11.1722f, -8.10398f);
 path.conicTo(22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f);
 path.conicTo(24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f);
@@ -439,7 +439,7 @@
 path.close();
 SkPath one(path);
 path.reset();
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(-1.54509f, -4.75528f);
 path.conicTo(22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f);
 path.conicTo(25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f);
@@ -453,7 +453,7 @@
 
 DEF_TEST(SixtyOvalsAX, reporter) {
 SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0x4132c174), SkBits2Float(0xc101a9e5));  // 11.1722f, -8.10398f
 path.conicTo(SkBits2Float(0x41b7508a), SkBits2Float(0xc1260efe), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736ad), SkBits2Float(0x3f811abd));  // 22.9143f, -10.3787f, 23.7764f, -7.72542f, 1.00863f
 path.conicTo(SkBits2Float(0x41c5564b), SkBits2Float(0xc09f7d6d), SkBits2Float(0x415d0934), SkBits2Float(0x3c880a93), SkBits2Float(0x3f79179a));  // 24.6671f, -4.98406f, 13.8147f, 0.0166066f, 0.973016f
@@ -481,7 +481,7 @@
 path.close();
 SkPath one(path);
 path.reset();
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0xbfc5c55c), SkBits2Float(0xc0982b46));  // -1.54509f, -4.75528f
 path.conicTo(SkBits2Float(0x41b1d9c2), SkBits2Float(0xc147b0fc), SkBits2Float(0x41be3618), SkBits2Float(0xc0f736b3), SkBits2Float(0x3f3504f3));  // 22.2313f, -12.4807f, 23.7764f, -7.72543f, 0.707107f
 path.conicTo(SkBits2Float(0x41ca926e), SkBits2Float(0xc03e16da), SkBits2Float(0x3fc5c55c), SkBits2Float(0x40982b46), SkBits2Float(0x3f3504f3));  // 25.3215f, -2.97014f, 1.54509f, 4.75528f, 0.707107f
diff --git a/tests/PathOpsBuilderTest.cpp b/tests/PathOpsBuilderTest.cpp
index 1d42010..31c02fe 100644
--- a/tests/PathOpsBuilderTest.cpp
+++ b/tests/PathOpsBuilderTest.cpp
@@ -25,26 +25,26 @@
     REPORTER_ASSERT(reporter, result.isEmpty());
 
     SkPath rectPath;
-    rectPath.setFillType(SkPath::kEvenOdd_FillType);
-    rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
+    rectPath.setFillType(SkPathFillType::kEvenOdd);
+    rectPath.addRect(0, 1, 2, 3, SkPathDirection::kCW);
     builder.add(rectPath, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     bool closed;
-    SkPath::Direction dir;
+    SkPathDirection dir;
     REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
-    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
+    REPORTER_ASSERT(reporter, dir == SkPathDirection::kCCW);
     int pixelDiff = comparePaths(reporter, __FUNCTION__, rectPath, result);
     REPORTER_ASSERT(reporter, pixelDiff == 0);
 
     rectPath.reset();
-    rectPath.setFillType(SkPath::kEvenOdd_FillType);
-    rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
+    rectPath.setFillType(SkPathFillType::kEvenOdd);
+    rectPath.addRect(0, 1, 2, 3, SkPathDirection::kCCW);
     builder.add(rectPath, kUnion_SkPathOp);
     REPORTER_ASSERT(reporter, builder.resolve(&result));
     REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
-    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
+    REPORTER_ASSERT(reporter, dir == SkPathDirection::kCCW);
     REPORTER_ASSERT(reporter, rectPath == result);
 
     builder.add(rectPath, kDifference_SkPathOp);
@@ -52,8 +52,8 @@
     REPORTER_ASSERT(reporter, result.isEmpty());
 
     SkPath rect2, rect3;
-    rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction);
-    rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction);
+    rect2.addRect(2, 1, 4, 3, SkPathDirection::kCW);
+    rect3.addRect(4, 1, 5, 3, SkPathDirection::kCCW);
     builder.add(rectPath, kUnion_SkPathOp);
     builder.add(rect2, kUnion_SkPathOp);
     builder.add(rect3, kUnion_SkPathOp);
@@ -65,9 +65,9 @@
     REPORTER_ASSERT(reporter, result.getBounds() == expected);
 
     SkPath circle1, circle2, circle3;
-    circle1.addCircle(5, 6, 4, SkPath::kCW_Direction);
-    circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction);
-    circle3.addCircle(6, 5, 6, SkPath::kCW_Direction);
+    circle1.addCircle(5, 6, 4, SkPathDirection::kCW);
+    circle2.addCircle(7, 4, 8, SkPathDirection::kCCW);
+    circle3.addCircle(6, 5, 6, SkPathDirection::kCW);
     SkPath opCompare;
     Op(circle1, circle2, kUnion_SkPathOp, &opCompare);
     Op(opCompare, circle3, kDifference_SkPathOp, &opCompare);
@@ -138,12 +138,12 @@
 
 DEF_TEST(BuilderIssue502792_2, reporter) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(2, 2, 3, 3, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction);
-    pathB.addRect(3, 3, 4, 4, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(2, 2, 3, 3, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(3, 3, 4, 4, SkPathDirection::kCW);
+    pathB.addRect(3, 3, 4, 4, SkPathDirection::kCW);
     SkOpBuilder builder;
     builder.add(path, kUnion_SkPathOp);
     builder.add(pathB, kDifference_SkPathOp);
@@ -304,14 +304,14 @@
 
 DEF_TEST(SkOpBuilderFuzz665, reporter) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50));  // -5.0959e+07f, 2.32235e+07f
 path.lineTo(SkBits2Float(0xcc4264b0), SkBits2Float(0x4bb12e48));  // -5.0959e+07f, 2.32234e+07f
 path.lineTo(SkBits2Float(0xcc4264a7), SkBits2Float(0x4bb12e50));  // -5.0959e+07f, 2.32235e+07f
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x43213333), SkBits2Float(0x43080000));  // 161.2f, 136
 path.lineTo(SkBits2Float(0x43038000), SkBits2Float(0x43080000));  // 131.5f, 136
 path.cubicTo(SkBits2Float(0x43038000), SkBits2Float(0x42f00000), SkBits2Float(0x42f16666), SkBits2Float(0x42d53333), SkBits2Float(0x42d3cccd), SkBits2Float(0x42cd6666));  // 131.5f, 120, 120.7f, 106.6f, 105.9f, 102.7f
diff --git a/tests/PathOpsChalkboardTest.cpp b/tests/PathOpsChalkboardTest.cpp
index 6c03f2c..b75dfdd 100644
--- a/tests/PathOpsChalkboardTest.cpp
+++ b/tests/PathOpsChalkboardTest.cpp
@@ -15,7 +15,6 @@
 
 static void chalkboard(skiatest::Reporter* reporter, uint64_t testlines) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
 uint64_t i = 0;
 path.moveTo(SkBits2Float(0x4470eed9), SkBits2Float(0x439c1ac1));  // 963.732f, 312.209f
 if (testlines & (1LL << i++)) path.lineTo(SkBits2Float(0x4470dde3), SkBits2Float(0x439c63d8));  // 963.467f, 312.78f
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index 45699d7..a9923d6 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -452,7 +452,7 @@
 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                   const char* pathStr) {
     static SkMutex& simplifyDebugOut = *(new SkMutex);
-    SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
+    SkPathFillType fillType = useXor ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding;
     path.setFillType(fillType);
     state.fReporter->bumpTestCount();
     if (!Simplify(path, &out)) {
@@ -469,8 +469,8 @@
         std::string str;
         const char* pathPrefix = nullptr;
         const char* nameSuffix = nullptr;
-        if (fillType == SkPath::kEvenOdd_FillType) {
-            pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
+        if (fillType == SkPathFillType::kEvenOdd) {
+            pathPrefix = "    path.setFillType(SkPathFillType::kEvenOdd);\n";
             nameSuffix = "x";
         }
         const char testFunction[] = "testSimplify(reporter, path);";
@@ -772,12 +772,12 @@
     }
 }
 
-void PathOpsThreadState::outputProgress(const char* pathStr, SkPath::FillType pathFillType) {
+void PathOpsThreadState::outputProgress(const char* pathStr, SkPathFillType pathFillType) {
     const char testFunction[] = "testSimplify(path);";
     const char* pathPrefix = nullptr;
     const char* nameSuffix = nullptr;
-    if (pathFillType == SkPath::kEvenOdd_FillType) {
-        pathPrefix = "    path.setFillType(SkPath::kEvenOdd_FillType);\n";
+    if (pathFillType == SkPathFillType::kEvenOdd) {
+        pathPrefix = "    path.setFillType(SkPathFillType::kEvenOdd);\n";
         nameSuffix = "x";
     }
     appendTest(pathStr, pathPrefix, nameSuffix, testFunction, false, fPathStr);
diff --git a/tests/PathOpsFuzz763Test.cpp b/tests/PathOpsFuzz763Test.cpp
index 3e864bf..1e50a9e 100644
--- a/tests/PathOpsFuzz763Test.cpp
+++ b/tests/PathOpsFuzz763Test.cpp
@@ -11,7 +11,7 @@
 
 static void fuzz763_3084(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
 path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
@@ -49,7 +49,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
 path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
@@ -67,7 +67,7 @@
 
 static void fuzz763_1823(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc19e6455), SkBits2Float(0xc19e6455));
 path.quadTo(SkBits2Float(0xc1399153), SkBits2Float(0xc1e00000), SkBits2Float(0x00000000), SkBits2Float(0xc1e00000));
 path.quadTo(SkBits2Float(0x41399153), SkBits2Float(0xc1e00000), SkBits2Float(0x419e6455), SkBits2Float(0xc19e6455));
@@ -104,7 +104,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x42040000));
 path.quadTo(SkBits2Float(0x41000000), SkBits2Float(0x4211413d), SkBits2Float(0x40b504f3), SkBits2Float(0x421aa09e));
 path.quadTo(SkBits2Float(0x405413cd), SkBits2Float(0x42240000), SkBits2Float(0x00000000), SkBits2Float(0x42240000));
@@ -122,7 +122,7 @@
 
 static void fuzz763_378(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41013776), SkBits2Float(0xc25007a8));
 path.quadTo(SkBits2Float(0x412f219e), SkBits2Float(0xc256a86f), SkBits2Float(0x41625842), SkBits2Float(0xc2533a60));
 path.quadTo(SkBits2Float(0x418ac776), SkBits2Float(0xc24fcc52), SkBits2Float(0x41980904), SkBits2Float(0xc24451c8));
@@ -198,7 +198,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
 path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c));
 path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62));
@@ -215,7 +215,7 @@
 
 static void fuzz763_378b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(-47.1494f, 4.35143f);
 path.quadTo(-39.8075f, 18.9486f, -43.0083f, 19.8062f);
 path.quadTo(-50.35f, 5.21042f, -52.0068f, 8.08022f);
@@ -225,7 +225,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2444fb0), SkBits2Float(0x419813d4));
 path.quadTo(SkBits2Float(0xc24fca68), SkBits2Float(0x418ad2e8), SkBits2Float(0xc25338d1), SkBits2Float(0x41626f8c));
 path.quadTo(SkBits2Float(0xc256a73a), SkBits2Float(0x412f3944), SkBits2Float(0xc25006c4), SkBits2Float(0x41014e62));
@@ -238,7 +238,7 @@
 
 static void fuzz763_378c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
     path.moveTo(-47.1494f, 4.35143f);
     path.quadTo(-46.208f, 20.6664f, -43.0072f, 19.8086f);
     path.quadTo(-39.8065f, 18.9507f, -38.1498f, 16.0809f);
@@ -247,7 +247,7 @@
     path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(-49.0778f, 19.0097f);
     path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f);
     path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f);
@@ -259,14 +259,14 @@
 
 static void fuzz763_378d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(-47.1494f, 4.35143f);
 path.quadTo(-38.2091f, 6.80749f, -37.3514f, 10.0083f);  // required
 path.quadTo(-36.4938f, 13.2091f, -38.1506f, 16.0788f);  // required
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(-49.0778f, 19.0097f);
 path.quadTo(-38.2087f, 6.80955f, -37.3509f, 10.0103f);
 path.quadTo(-36.4931f, 13.211f, -38.1498f, 16.0809f);
@@ -277,7 +277,7 @@
 
 static void fuzz763_558(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41c95d06), SkBits2Float(0xc238e312));
 path.quadTo(SkBits2Float(0x41e37302), SkBits2Float(0xc23b3f66), SkBits2Float(0x41f93bb2), SkBits2Float(0xc233b1b3));
 path.quadTo(SkBits2Float(0x42025d9e), SkBits2Float(0xc22fb50a), SkBits2Float(0x4205bcea), SkBits2Float(0xc22a30db));
@@ -353,7 +353,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xbfe9fe20), SkBits2Float(0x42526568));
 path.quadTo(SkBits2Float(0xc08f5cf4), SkBits2Float(0x425a57e7), SkBits2Float(0xc0f853f0), SkBits2Float(0x4258763b));
 path.quadTo(SkBits2Float(0xc130a57c), SkBits2Float(0x42569490), SkBits2Float(0xc1506f76), SkBits2Float(0x424bf8e3));
@@ -371,7 +371,7 @@
 
 static void fuzz763_378a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x40f4c1fc), SkBits2Float(0xc25049c6));
 path.quadTo(SkBits2Float(0x41281306), SkBits2Float(0xc25702a0), SkBits2Float(0x415b6610), SkBits2Float(0xc253af82));
 path.quadTo(SkBits2Float(0x41875c90), SkBits2Float(0xc2505c66), SkBits2Float(0x4194ce44), SkBits2Float(0xc244efe4));
@@ -441,7 +441,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
 path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
 path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296));
@@ -460,7 +460,7 @@
 
 static void fuzz763_378a_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
 path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
 path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
@@ -482,7 +482,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc243ae22), SkBits2Float(0x419b4beb));
 path.quadTo(SkBits2Float(0xc24f36b0), SkBits2Float(0x418e3b60), SkBits2Float(0xc252bffc), SkBits2Float(0x41695dc8));
 path.quadTo(SkBits2Float(0xc2564948), SkBits2Float(0x413644ce), SkBits2Float(0xc24fc102), SkBits2Float(0x41082296));
@@ -500,7 +500,7 @@
 
 static void fuzz763_8712(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x40dce520), SkBits2Float(0xc250b45c));
 path.quadTo(SkBits2Float(0x411bc0ec), SkBits2Float(0xc25796e0), SkBits2Float(0x414f4352), SkBits2Float(0xc25472d6));
 path.quadTo(SkBits2Float(0x418162dd), SkBits2Float(0xc2514ece), SkBits2Float(0x418f27e4), SkBits2Float(0xc245fb37));
@@ -572,7 +572,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
 path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
 path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c));
@@ -590,7 +590,7 @@
 
 static void fuzz763_8712a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc23c98fa), SkBits2Float(0x408b3eec));
 path.quadTo(SkBits2Float(0xc22fcb5c), SkBits2Float(0x405f9a18), SkBits2Float(0xc22450bb), SkBits2Float(0x40a4d200));
 path.quadTo(SkBits2Float(0xc218d61a), SkBits2Float(0x40d9d6f4), SkBits2Float(0xc21567dd), SkBits2Float(0x412021ef));
@@ -607,7 +607,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2428ca4), SkBits2Float(0x41a0e11e));
 path.quadTo(SkBits2Float(0xc24e2cd0), SkBits2Float(0x41942565), SkBits2Float(0xc251e4cc), SkBits2Float(0x417566f6));
 path.quadTo(SkBits2Float(0xc2559cca), SkBits2Float(0x4142831e), SkBits2Float(0xc24f3eed), SkBits2Float(0x4114026c));
@@ -625,7 +625,7 @@
 
 static void fuzz763_4014(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x4126977e), SkBits2Float(0xc24e5cc8));
 path.quadTo(SkBits2Float(0x4155a79e), SkBits2Float(0xc2547762), SkBits2Float(0x41841952), SkBits2Float(0xc250767b));
 path.quadTo(SkBits2Float(0x419d5ed4), SkBits2Float(0xc24c7594), SkBits2Float(0x41a99408), SkBits2Float(0xc240b18c));
@@ -696,7 +696,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));
 path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));
 path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));
@@ -714,7 +714,7 @@
 
 static void fuzz763_4014a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc23d30a0), SkBits2Float(0x400e5c28));  // -47.2975f, 2.22437f
 path.quadTo(SkBits2Float(0xc2303ecc), SkBits2Float(0x3fc17f10), SkBits2Float(0xc2251387), SkBits2Float(0x4052f2a8));  // -44.0613f, 1.51169f, -41.2691f, 3.29606f
 path.quadTo(SkBits2Float(0xc219e842), SkBits2Float(0x40a292e2), SkBits2Float(0xc2170e78), SkBits2Float(0x410510c3));  // -38.4768f, 5.08043f, -37.7641f, 8.31659f
@@ -732,7 +732,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2478a00), SkBits2Float(0x418639e0));  // -49.8848f, 16.7783f
 path.quadTo(SkBits2Float(0xc252b546), SkBits2Float(0x416fe6f8), SkBits2Float(0xc2558f0e), SkBits2Float(0x413c1fa8));  // -52.677f, 14.9939f, -53.3897f, 11.7577f
 path.quadTo(SkBits2Float(0xc25868d8), SkBits2Float(0x41085856), SkBits2Float(0xc25145a6), SkBits2Float(0x40b75684));  // -54.1024f, 8.52157f, -52.318f, 5.72931f
@@ -750,7 +750,7 @@
 
 static void fuzz763_1404(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x419b2e3e), SkBits2Float(0xc243b405));
 path.quadTo(SkBits2Float(0x41b4811d), SkBits2Float(0xc2479f9a), SkBits2Float(0x41cbf476), SkBits2Float(0xc2417131));
 path.quadTo(SkBits2Float(0x41e19882), SkBits2Float(0xc23bbceb), SkBits2Float(0x41e9f15f), SkBits2Float(0xc23083cb));
@@ -833,7 +833,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x401b14b8), SkBits2Float(0x42524cc7));
 path.quadTo(SkBits2Float(0xbd754800), SkBits2Float(0x425b13d0), SkBits2Float(0xc05781d0), SkBits2Float(0x425a42cf));
 path.quadTo(SkBits2Float(0xc0d59744), SkBits2Float(0x425971cf), SkBits2Float(0xc10de7c8), SkBits2Float(0x424f8332));
@@ -851,7 +851,7 @@
 
 static void fuzz763_4713(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x40f7bc68), SkBits2Float(0xc2503bb0));
 path.quadTo(SkBits2Float(0x41299c84), SkBits2Float(0xc256ef4e), SkBits2Float(0x415ce976), SkBits2Float(0xc2539652));
 path.quadTo(SkBits2Float(0x41881b33), SkBits2Float(0xc2503d58), SkBits2Float(0x41958271), SkBits2Float(0xc244cdc4));
@@ -919,7 +919,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
 path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
@@ -937,7 +937,7 @@
 
 static void fuzz763_24588(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x413a5194), SkBits2Float(0xc24d4e33));  // 11.6449f, -51.3264f
 path.quadTo(SkBits2Float(0x4169f3fc), SkBits2Float(0xc2532032), SkBits2Float(0x418e0c8b), SkBits2Float(0xc24ed218));  // 14.6221f, -52.7814f, 17.7561f, -51.7052f
 path.quadTo(SkBits2Float(0x41a71f17), SkBits2Float(0xc24a83ff), SkBits2Float(0x41b2c316), SkBits2Float(0xc23e9b65));  // 20.8902f, -50.6289f, 22.3453f, -47.6518f
@@ -1012,7 +1012,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x421a8288), SkBits2Float(0x420efdef));  // 38.6275f, 35.748f
 path.quadTo(SkBits2Float(0x4219989b), SkBits2Float(0x421c3719), SkBits2Float(0x420f9986), SkBits2Float(0x4224eb5c));  // 38.399f, 39.0538f, 35.8999f, 41.2298f
 path.quadTo(SkBits2Float(0x42059a71), SkBits2Float(0x422d9f9f), SkBits2Float(0x41f0c28e), SkBits2Float(0x422cb5b2));  // 33.4008f, 43.4059f, 30.095f, 43.1774f
@@ -1031,7 +1031,7 @@
 // SkDQuadIntersection.cpp:594: failed assertion "way_roughly_zero(fT[0][index])
 static void fuzz763_20016(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41e88c66), SkBits2Float(0xc22f800b));  // 29.0686f, -43.875f
 path.quadTo(SkBits2Float(0x420178e8), SkBits2Float(0xc230b9b9), SkBits2Float(0x420babd1), SkBits2Float(0xc228426b));  // 32.3681f, -44.1814f, 34.9178f, -42.0649f
 path.quadTo(SkBits2Float(0x4215debb), SkBits2Float(0xc21fcb1e), SkBits2Float(0x42171869), SkBits2Float(0xc2129869));  // 37.4675f, -39.9484f, 37.7738f, -36.6488f
@@ -1115,7 +1115,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41bbb37b), SkBits2Float(0x423c724c));  // 23.4626f, 47.1116f
 path.quadTo(SkBits2Float(0x41b0a015), SkBits2Float(0x42487d2f), SkBits2Float(0x4197c34c), SkBits2Float(0x424d16a6));  // 22.0782f, 50.1222f, 18.9704f, 51.2721f
 path.quadTo(SkBits2Float(0x417dcd04), SkBits2Float(0x4251b01e), SkBits2Float(0x414da178), SkBits2Float(0x424c266a));  // 15.8626f, 52.422f, 12.8519f, 51.0375f
@@ -1133,7 +1133,7 @@
 
 static void fuzz763_17370(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41fb8980), SkBits2Float(0xc20d9cf4));  // 31.4421f, -35.4033f
 path.quadTo(SkBits2Float(0x42081e43), SkBits2Float(0xc215e4e6), SkBits2Float(0x42154ac6), SkBits2Float(0xc2146e6f));  // 34.0296f, -37.4735f, 37.323f, -37.1078f
 path.quadTo(SkBits2Float(0x4222774a), SkBits2Float(0xc212f7f8), SkBits2Float(0x422abf3a), SkBits2Float(0xc2089e76));  // 40.6165f, -36.7422f, 42.6867f, -34.1547f
@@ -1223,7 +1223,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x415798ee), SkBits2Float(0x424b81a4));  // 13.4748f, 50.8766f
 path.quadTo(SkBits2Float(0x41382eba), SkBits2Float(0x42562f18), SkBits2Float(0x4103c4ac), SkBits2Float(0x42582e42));  // 11.5114f, 53.546f, 8.23552f, 54.0452f
 path.quadTo(SkBits2Float(0x409eb53c), SkBits2Float(0x425a2d6e), SkBits2Float(0x40129340), SkBits2Float(0x425252e0));  // 4.95962f, 54.5444f, 2.29024f, 52.5809f
@@ -1242,7 +1242,7 @@
 // SkDQuadIntersection.cpp:598: failed assertion "way_roughly_equal(fT[0][index], 1)"
 static void fuzz763_35322(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41042400), SkBits2Float(0xc24fea42));  // 8.25879f, -51.9788f
 path.quadTo(SkBits2Float(0x413225f2), SkBits2Float(0xc25680b3), SkBits2Float(0x4165502a), SkBits2Float(0xc2530720));  // 11.1343f, -53.6257f, 14.3321f, -52.757f
 path.quadTo(SkBits2Float(0x418c3d32), SkBits2Float(0xc24f8d8e), SkBits2Float(0x41996a13), SkBits2Float(0xc2440d11));  // 17.5299f, -51.8882f, 19.1768f, -49.0128f
@@ -1330,7 +1330,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41213a06), SkBits2Float(0x424ea0eb));  // 10.0767f, 51.6571f
 path.quadTo(SkBits2Float(0x40fe1b10), SkBits2Float(0x4258c340), SkBits2Float(0x40947314), SkBits2Float(0x4259e330));  // 7.9408f, 54.1907f, 4.63905f, 54.4719f
 path.quadTo(SkBits2Float(0x3fab2c60), SkBits2Float(0x425b0321), SkBits2Float(0xbf991e40), SkBits2Float(0x42527802));  // 1.33729f, 54.7531f, -1.19624f, 52.6172f
@@ -1349,7 +1349,7 @@
 // SkPathOpsOp.cpp:52: failed assertion "angle != firstAngle || !loop"
 static void fuzz763_849020(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x411e4374), SkBits2Float(0xc24ec58f));  // 9.89147f, -51.6929f
 path.quadTo(SkBits2Float(0x414d13fa), SkBits2Float(0xc254fe70), SkBits2Float(0x417fc7a6), SkBits2Float(0xc2511e30));  // 12.8174f, -53.2485f, 15.9862f, -52.2795f
 path.quadTo(SkBits2Float(0x41993dab), SkBits2Float(0xc24d3df4), SkBits2Float(0x41a5af6e), SkBits2Float(0xc24189d2));  // 19.1551f, -51.3105f, 20.7107f, -48.3846f
@@ -1423,7 +1423,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x421f4961), SkBits2Float(0x4209a6a0));  // 39.8217f, 34.4127f
 path.quadTo(SkBits2Float(0x421ed2ca), SkBits2Float(0x4216e5cb), SkBits2Float(0x42152104), SkBits2Float(0x421fefda));  // 39.7058f, 37.7244f, 37.2822f, 39.9842f
 path.quadTo(SkBits2Float(0x420b6f41), SkBits2Float(0x4228f9ea), SkBits2Float(0x41fc602b), SkBits2Float(0x42288353));  // 34.8586f, 42.2441f, 31.547f, 42.1282f
@@ -1441,7 +1441,7 @@
 
 static void fuzz763_1597464(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x4101092a), SkBits2Float(0xc2500973));  // 8.06474f, -52.0092f
 path.quadTo(SkBits2Float(0x412ef1d8), SkBits2Float(0xc256aade), SkBits2Float(0x41622940), SkBits2Float(0xc2533d84));  // 10.934f, -53.6669f, 14.1351f, -52.8101f
 path.quadTo(SkBits2Float(0x418ab055), SkBits2Float(0xc24fd02d), SkBits2Float(0x4197f32a), SkBits2Float(0xc2445602));  // 17.3361f, -51.9533f, 18.9937f, -49.084f
@@ -1515,7 +1515,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc2444b74), SkBits2Float(0x419829ad));  // -49.0737f, 19.0203f
 path.quadTo(SkBits2Float(0xc24fc68b), SkBits2Float(0x418aea08), SkBits2Float(0xc25335aa), SkBits2Float(0x41629e8a));  // -51.9439f, 17.3643f, -52.8024f, 14.1637f
 path.quadTo(SkBits2Float(0xc256a4ca), SkBits2Float(0x412f6908), SkBits2Float(0xc25004f8), SkBits2Float(0x41017cac));  // -53.6609f, 10.9631f, -52.0049f, 8.09294f
@@ -1533,7 +1533,7 @@
 
 static void fuzz763_34974(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 #if 00
 path.moveTo(SkBits2Float(0x41015326), SkBits2Float(0xc2500694));
 path.quadTo(SkBits2Float(0x412f3e30), SkBits2Float(0xc256a6fa), SkBits2Float(0x41627462), SkBits2Float(0xc253387e));
@@ -1625,7 +1625,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 #if 01
 path.moveTo(SkBits2Float(0xc2445236), SkBits2Float(0x419806c2));
 path.quadTo(SkBits2Float(0xc24fccb6), SkBits2Float(0x418ac513), SkBits2Float(0xc2533ab2), SkBits2Float(0x4162536e));
@@ -1644,7 +1644,7 @@
 
 static void fuzz763_2211264(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41017a68), SkBits2Float(0xc250050e));
 path.quadTo(SkBits2Float(0x412f66b2), SkBits2Float(0xc256a4e9), SkBits2Float(0x41629c3c), SkBits2Float(0xc25335d1));
 path.quadTo(SkBits2Float(0x418ae8e6), SkBits2Float(0xc24fc6bc), SkBits2Float(0x4198289b), SkBits2Float(0xc2444ba9));
@@ -1701,7 +1701,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc24455cc), SkBits2Float(0x4197f43e));
 path.quadTo(SkBits2Float(0xc24fcffc), SkBits2Float(0x418ab179), SkBits2Float(0xc2533d5e), SkBits2Float(0x41622b92));
 path.quadTo(SkBits2Float(0xc256aac0), SkBits2Float(0x412ef432), SkBits2Float(0xc250095d), SkBits2Float(0x41010b70));
@@ -1719,7 +1719,7 @@
 
 static void fuzz763_4628016(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x41029678), SkBits2Float(0xc24ff9f4));  // 8.16174f, -51.9941f
 path.quadTo(SkBits2Float(0x41308bcc), SkBits2Float(0xc25695e3), SkBits2Float(0x4163bca4), SkBits2Float(0xc253226d));  // 11.0341f, -53.6464f, 14.2336f, -52.7836f
 path.quadTo(SkBits2Float(0x4164114c), SkBits2Float(0xc2531cb8), SkBits2Float(0x41646770), SkBits2Float(0xc25316ce));  // 14.2542f, -52.778f, 14.2753f, -52.7723f
@@ -1794,7 +1794,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4223940b), SkBits2Float(0x420485b1));  // 40.8946f, 33.1306f
 path.quadTo(SkBits2Float(0x4223893a), SkBits2Float(0x4211c6ea), SkBits2Float(0x421a2234), SkBits2Float(0x421b1ea2));  // 40.884f, 36.4443f, 38.5334f, 38.7799f
 path.quadTo(SkBits2Float(0x4210bb30), SkBits2Float(0x4224765a), SkBits2Float(0x420379f7), SkBits2Float(0x42246b89));  // 36.1828f, 41.1156f, 32.8691f, 41.105f
@@ -1812,7 +1812,7 @@
 
 static void fuzz763_6411089(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x410373c2), SkBits2Float(0xc24ff13e));
 path.quadTo(SkBits2Float(0x4131701e), SkBits2Float(0xc2568a1e), SkBits2Float(0x41649d46), SkBits2Float(0xc2531340));
 path.quadTo(SkBits2Float(0x418be539), SkBits2Float(0xc24f9c64), SkBits2Float(0x419916f9), SkBits2Float(0xc2441d4e));
@@ -1887,7 +1887,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
 path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
 path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
@@ -1905,7 +1905,7 @@
 
 static void fuzz763_3283699(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x411032d0), SkBits2Float(0xc24f69e5));
 path.quadTo(SkBits2Float(0x413e956c), SkBits2Float(0xc255d56a), SkBits2Float(0x41718a9c), SkBits2Float(0xc2522c67));
 path.quadTo(SkBits2Float(0x41755c61), SkBits2Float(0xc251e62c), SkBits2Float(0x417909ca), SkBits2Float(0xc2519487));
@@ -1987,7 +1987,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc24450bb), SkBits2Float(0x41980e6c));
 path.quadTo(SkBits2Float(0xc24fcb5c), SkBits2Float(0x418acd2f), SkBits2Float(0xc2533998), SkBits2Float(0x416263e8));
 path.quadTo(SkBits2Float(0xc256a7d4), SkBits2Float(0x412f2d72), SkBits2Float(0xc2500736), SkBits2Float(0x410142ec));
@@ -2005,7 +2005,7 @@
 
 static void fuzz763_1026368(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x4101c02c), SkBits2Float(0xc2500256));  // 8.10942f, -52.0023f
 path.quadTo(SkBits2Float(0x412faeae), SkBits2Float(0xc256a13a), SkBits2Float(0x4162e312), SkBits2Float(0xc2533110));  // 10.9801f, -53.6574f, 14.1804f, -52.7979f
 path.quadTo(SkBits2Float(0x418b0bbc), SkBits2Float(0xc24fc0e8), SkBits2Float(0x41984984), SkBits2Float(0xc2444547));  // 17.3807f, -51.9384f, 19.0359f, -49.0677f
@@ -2079,7 +2079,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 33
 path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
@@ -2098,7 +2098,7 @@
 
 static void fuzz763_5485218(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xc1b1a434), SkBits2Float(0xc247d348));
 path.quadTo(SkBits2Float(0xc1996ac1), SkBits2Float(0xc24d3588), SkBits2Float(0xc180ac87), SkBits2Float(0xc248738e));
 path.quadTo(SkBits2Float(0xc14fdc9c), SkBits2Float(0xc243b194), SkBits2Float(0xc13a53a0), SkBits2Float(0xc23794da));
@@ -2186,7 +2186,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x425285c7), SkBits2Float(0xbdfdd900));
 path.quadTo(SkBits2Float(0x425adb97), SkBits2Float(0x401cf6e8), SkBits2Float(0x425976d3), SkBits2Float(0x40b7eee0));
 path.quadTo(SkBits2Float(0x42581210), SkBits2Float(0x4110b128), SkBits2Float(0x424dc3b4), SkBits2Float(0x41320868));
@@ -2204,7 +2204,7 @@
 
 static void fuzz763_2674194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0xbfb16e10), SkBits2Float(0xc252733b));
 path.quadTo(SkBits2Float(0x3f91df50), SkBits2Float(0xc25b07b4), SkBits2Float(0x408e27f4), SkBits2Float(0xc259f3f8));
 path.quadTo(SkBits2Float(0x40f7d814), SkBits2Float(0xc258e03e), SkBits2Float(0x411e3df0), SkBits2Float(0xc24ec5d2));
@@ -2293,7 +2293,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41828cb4), SkBits2Float(0x424825e0));
 path.quadTo(SkBits2Float(0x416823bc), SkBits2Float(0x42534038), SkBits2Float(0x41344220), SkBits2Float(0x4255fb7e));
 path.quadTo(SkBits2Float(0x41006082), SkBits2Float(0x4258b6c5), SkBits2Float(0x40a7ee48), SkBits2Float(0x4251795a));
@@ -2311,7 +2311,7 @@
 
 static void fuzz763_10022998(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x40f23d54), SkBits2Float(0xc250558c));  // 7.56999f, -52.0835f
 path.quadTo(SkBits2Float(0x4126c646), SkBits2Float(0xc25712d1), SkBits2Float(0x415a1e76), SkBits2Float(0xc253c4aa));  // 10.4234f, -53.7684f, 13.6324f, -52.9421f
 path.quadTo(SkBits2Float(0x4186bb52), SkBits2Float(0xc2507686), SkBits2Float(0x419435db), SkBits2Float(0xc2450c9e));  // 16.8415f, -52.1157f, 18.5263f, -49.2623f
@@ -2379,7 +2379,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));  // 41, 33
 path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));  // 41, 36.3137f, 38.6569f, 38.6569f
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));  // 36.3137f, 41, 33, 41
diff --git a/tests/PathOpsInverseTest.cpp b/tests/PathOpsInverseTest.cpp
index 6f0034b..e9d8765 100644
--- a/tests/PathOpsInverseTest.cpp
+++ b/tests/PathOpsInverseTest.cpp
@@ -7,22 +7,24 @@
 #include "tests/PathOpsExtendedTest.h"
 
 DEF_TEST(PathOpsInverse, reporter) {
+    const SkPathDirection dirs[] = {SkPathDirection::kCW, SkPathDirection::kCCW};
+    const SkPathFillType fts[] = {
+        SkPathFillType::kWinding,        SkPathFillType::kEvenOdd,
+        SkPathFillType::kInverseWinding, SkPathFillType::kInverseEvenOdd
+    };
     SkPath one, two;
     int testCount = 0;
     for (int op = kDifference_SkPathOp; op <= kReverseDifference_SkPathOp; ++op) {
-        for (int oneFill = SkPath::kWinding_FillType; oneFill <= SkPath::kInverseEvenOdd_FillType;
-                    ++oneFill) {
-            for (int oneDir = SkPath::kCW_Direction; oneDir != SkPath::kCCW_Direction; ++oneDir) {
+        for (auto oneFill : fts) {
+            for (auto oneDir : dirs) {
                 one.reset();
-                one.setFillType((SkPath::FillType) oneFill);
-                one.addRect(0, 0, 6, 6, (SkPath::Direction) oneDir);
-                for (int twoFill = SkPath::kWinding_FillType;
-                        twoFill <= SkPath::kInverseEvenOdd_FillType; ++twoFill) {
-                    for (int twoDir = SkPath::kCW_Direction; twoDir != SkPath::kCCW_Direction;
-                            ++twoDir) {
+                one.setFillType(oneFill);
+                one.addRect(0, 0, 6, 6, oneDir);
+                for (auto twoFill : fts) {
+                    for (auto twoDir : dirs) {
                         two.reset();
-                        two.setFillType((SkPath::FillType) twoFill);
-                        two.addRect(3, 3, 9, 9, (SkPath::Direction) twoDir);
+                        two.setFillType(twoFill);
+                        two.addRect(3, 3, 9, 9, twoDir);
                         SkString testName;
                         testName.printf("inverseTest%d", ++testCount);
                         testPathOp(reporter, one, two, (SkPathOp) op, testName.c_str());
diff --git a/tests/PathOpsOpCircleThreadedTest.cpp b/tests/PathOpsOpCircleThreadedTest.cpp
index cb99acd..0ac492b 100644
--- a/tests/PathOpsOpCircleThreadedTest.cpp
+++ b/tests/PathOpsOpCircleThreadedTest.cpp
@@ -16,36 +16,37 @@
 
 static void testOpCirclesMain(PathOpsThreadState* data) {
         SkASSERT(data);
+    const SkPathFillType fts[] = { SkPathFillType::kWinding, SkPathFillType::kEvenOdd };
     PathOpsThreadState& state = *data;
     SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1 ; d < 7; ++d) {
-                    for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
-    for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
+                    for (auto e : fts) {
+    for (auto f : fts) {
         SkPath pathA, pathB;
-        pathA.setFillType((SkPath::FillType) e);
+        pathA.setFillType(e);
         pathA.addCircle(SkIntToScalar(state.fA), SkIntToScalar(state.fB), SkIntToScalar(state.fC),
-                state.fD ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
-        pathB.setFillType((SkPath::FillType) f);
+                state.fD ? SkPathDirection::kCW : SkPathDirection::kCCW);
+        pathB.setFillType(f);
         pathB.addCircle(SkIntToScalar(a), SkIntToScalar(b), SkIntToScalar(c),
-                d ? SkPath::kCW_Direction : SkPath::kCCW_Direction);
+                d ? SkPathDirection::kCW : SkPathDirection::kCCW);
         for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
             if (state.fReporter->verbose()) {
                 pathStr.printf("static void circlesOp%d(skiatest::Reporter* reporter,"
                         " const char* filename) {\n", loopNo);
                 pathStr.appendf("    SkPath path, pathB;\n");
-                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
-                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                pathStr.appendf("    path.setFillType(SkPathFillType::k%s);\n",
+                        e == SkPathFillType::kWinding ? "Winding" : e == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    path.addCircle(%d, %d, %d, %s);\n", state.fA, state.fB,
-                        state.fC, state.fD ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
-                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
-                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        state.fC, state.fD ? "SkPathDirection::kCW" : "SkPathDirection::kCCW");
+                pathStr.appendf("    pathB.setFillType(SkPathFillType::k%s);\n",
+                        f == SkPathFillType::kWinding ? "Winding" : f == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    pathB.addCircle(%d, %d, %d, %s);\n", a, b,
-                        c, d ? "SkPath::kCW_Direction" : "SkPath::kCCW_Direction");
+                        c, d ? "SkPathDirection::kCW" : "SkPathDirection::kCCW");
                 pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
                         SkPathOpsDebug::OpStr((SkPathOp) op));
                 pathStr.appendf("}\n");
diff --git a/tests/PathOpsOpCubicThreadedTest.cpp b/tests/PathOpsOpCubicThreadedTest.cpp
index 50eb414..e453483 100644
--- a/tests/PathOpsOpCubicThreadedTest.cpp
+++ b/tests/PathOpsOpCubicThreadedTest.cpp
@@ -19,21 +19,22 @@
     strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
 #endif
     SkASSERT(data);
+    const SkPathFillType fts[] = { SkPathFillType::kWinding, SkPathFillType::kEvenOdd };
     PathOpsThreadState& state = *data;
     SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1 ; d < 7; ++d) {
-                    for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
-    for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f) {
+                    for (auto e : fts) {
+    for (auto f : fts) {
         SkPath pathA, pathB;
-        pathA.setFillType((SkPath::FillType) e);
+        pathA.setFillType((SkPathFillType) e);
         pathA.moveTo(SkIntToScalar(state.fA), SkIntToScalar(state.fB));
         pathA.cubicTo(SkIntToScalar(state.fC), SkIntToScalar(state.fD), SkIntToScalar(b),
                 SkIntToScalar(a), SkIntToScalar(d), SkIntToScalar(c));
         pathA.close();
-        pathB.setFillType((SkPath::FillType) f);
+        pathB.setFillType((SkPathFillType) f);
         pathB.moveTo(SkIntToScalar(a), SkIntToScalar(b));
         pathB.cubicTo(SkIntToScalar(c), SkIntToScalar(d), SkIntToScalar(state.fB),
                 SkIntToScalar(state.fA), SkIntToScalar(state.fD), SkIntToScalar(state.fC));
@@ -43,15 +44,15 @@
                 pathStr.printf("static void cubicOp%d(skiatest::Reporter* reporter,"
                         " const char* filename) {\n", loopNo);
                 pathStr.appendf("    SkPath path, pathB;\n");
-                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
-                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                pathStr.appendf("    path.setFillType(SkPathFillType::k%s);\n",
+                        e == SkPathFillType::kWinding ? "Winding" : e == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    path.moveTo(%d,%d);\n", state.fA, state.fB);
                 pathStr.appendf("    path.cubicTo(%d,%d, %d,%d, %d,%d);\n", state.fC, state.fD,
                         b, a, d, c);
                 pathStr.appendf("    path.close();\n");
-                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
-                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                pathStr.appendf("    pathB.setFillType(SkPathFillType::k%s);\n",
+                        f == SkPathFillType::kWinding ? "Winding" : f == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    pathB.moveTo(%d,%d);\n", a, b);
                 pathStr.appendf("    pathB.cubicTo(%d,%d, %d,%d, %d,%d);\n", c, d,
diff --git a/tests/PathOpsOpRectThreadedTest.cpp b/tests/PathOpsOpRectThreadedTest.cpp
index 059ece2..b3809e0 100644
--- a/tests/PathOpsOpRectThreadedTest.cpp
+++ b/tests/PathOpsOpRectThreadedTest.cpp
@@ -23,26 +23,27 @@
 static void testPathOpsRectsMain(PathOpsThreadState* data)
 {
     SkASSERT(data);
+    const SkPathFillType fts[] = { SkPathFillType::kWinding, SkPathFillType::kEvenOdd };
     PathOpsThreadState& state = *data;
     SkString pathStr;
     for (int a = 0 ; a < 6; ++a) {
         for (int b = a + 1 ; b < 7; ++b) {
             for (int c = 0 ; c < 6; ++c) {
                 for (int d = c + 1 ; d < 7; ++d) {
-                    for (int e = SkPath::kWinding_FillType ; e <= SkPath::kEvenOdd_FillType; ++e) {
-    for (int f = SkPath::kWinding_FillType ; f <= SkPath::kEvenOdd_FillType; ++f)   {
+                    for (auto e : fts) {
+    for (auto f : fts)   {
         SkPath pathA, pathB;
-        pathA.setFillType((SkPath::FillType) e);
+        pathA.setFillType((SkPathFillType) e);
         pathA.addRect(SkIntToScalar(state.fA), SkIntToScalar(state.fA), SkIntToScalar(state.fB),
-                SkIntToScalar(state.fB), SkPath::kCW_Direction);
+                SkIntToScalar(state.fB), SkPathDirection::kCW);
         pathA.addRect(SkIntToScalar(state.fC), SkIntToScalar(state.fC), SkIntToScalar(state.fD),
-                SkIntToScalar(state.fD), SkPath::kCW_Direction);
+                SkIntToScalar(state.fD), SkPathDirection::kCW);
         pathA.close();
-        pathB.setFillType((SkPath::FillType) f);
+        pathB.setFillType((SkPathFillType) f);
         pathB.addRect(SkIntToScalar(a), SkIntToScalar(a), SkIntToScalar(b),
-                SkIntToScalar(b), SkPath::kCW_Direction);
+                SkIntToScalar(b), SkPathDirection::kCW);
         pathB.addRect(SkIntToScalar(c), SkIntToScalar(c), SkIntToScalar(d),
-                SkIntToScalar(d), SkPath::kCW_Direction);
+                SkIntToScalar(d), SkPathDirection::kCW);
         pathB.close();
         for (int op = 0 ; op <= kXOR_SkPathOp; ++op)    {
             if (state.fReporter->verbose()) {
@@ -50,20 +51,20 @@
                         "static void rects%d(skiatest::Reporter* reporter,"
                         "const char* filename) {\n", loopNo);
                 pathStr.appendf("    SkPath path, pathB;");
-                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n",
-                        e == SkPath::kWinding_FillType ? "Winding" : e == SkPath::kEvenOdd_FillType
+                pathStr.appendf("    path.setFillType(SkPathFillType::k%s);\n",
+                        e == SkPathFillType::kWinding ? "Winding" : e == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB, state.fB);
+                        " SkPathDirection::kCW);\n", state.fA, state.fA, state.fB, state.fB);
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD, state.fD);
-                pathStr.appendf("    pathB.setFillType(SkPath::k%s_FillType);\n",
-                        f == SkPath::kWinding_FillType ? "Winding" : f == SkPath::kEvenOdd_FillType
+                        " SkPathDirection::kCW);\n", state.fC, state.fC, state.fD, state.fD);
+                pathStr.appendf("    pathB.setFillType(SkPathFillType::k%s);\n",
+                        f == SkPathFillType::kWinding ? "Winding" : f == SkPathFillType::kEvenOdd
                         ? "EvenOdd" : "?UNDEFINED");
                 pathStr.appendf("    pathB.addRect(%d, %d, %d, %d,"
-                        " SkPath::kCW_Direction);\n", a, a, b, b);
+                        " SkPathDirection::kCW);\n", a, a, b, b);
                 pathStr.appendf("    pathB.addRect(%d, %d, %d, %d,"
-                        " SkPath::kCW_Direction);\n", c, c, d, d);
+                        " SkPathDirection::kCW);\n", c, c, d, d);
                 pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
                         SkPathOpsDebug::OpStr((SkPathOp) op));
                 pathStr.appendf("}\n\n");
@@ -111,6 +112,10 @@
 static void testPathOpsFastMain(PathOpsThreadState* data)
 {
     SkASSERT(data);
+    const SkPathFillType fts[] = {
+        SkPathFillType::kWinding,        SkPathFillType::kEvenOdd,
+        SkPathFillType::kInverseWinding, SkPathFillType::kInverseEvenOdd
+    };
     PathOpsThreadState& state = *data;
     SkString pathStr;
     int step = data->fReporter->allowExtendedTest() ? 2 : 5;
@@ -118,19 +123,19 @@
         for (bool b : { false, true } ) {
             for (int c = 0; c < 6; c += step) {
                 for (int d = 0; d < 6; d += step) {
-        for (int e = SkPath::kWinding_FillType; e <= SkPath::kInverseEvenOdd_FillType; ++e) {
-            for (int f = SkPath::kWinding_FillType; f <= SkPath::kInverseEvenOdd_FillType; ++f) {
+        for (auto e : fts) {
+            for (auto f : fts) {
         SkPath pathA, pathB;
-        pathA.setFillType((SkPath::FillType) e);
+        pathA.setFillType(e);
         if (a) {
         pathA.addRect(SkIntToScalar(state.fA), SkIntToScalar(state.fA), SkIntToScalar(state.fB) + c,
-                SkIntToScalar(state.fB), SkPath::kCW_Direction);
+                SkIntToScalar(state.fB), SkPathDirection::kCW);
         }
         pathA.close();
-        pathB.setFillType((SkPath::FillType) f);
+        pathB.setFillType(f);
         if (b) {
         pathB.addRect(SkIntToScalar(state.fC), SkIntToScalar(state.fC), SkIntToScalar(state.fD) + d,
-                SkIntToScalar(state.fD), SkPath::kCW_Direction);
+                SkIntToScalar(state.fD), SkPathDirection::kCW);
         }
         pathB.close();
         const char* fillTypeStr[] = { "Winding", "EvenOdd", "InverseWinding", "InverseEvenOdd" };
@@ -140,15 +145,15 @@
                         "static void fast%d(skiatest::Reporter* reporter,"
                         "const char* filename) {\n", loopNo);
                 pathStr.appendf("    SkPath path, pathB;");
-                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n", fillTypeStr[e]);
+                pathStr.appendf("    path.setFillType(SkPathFillType::k%s);\n", fillTypeStr[(int)e]);
                 if (a) {
                     pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                          " SkPath::kCW_Direction);\n", state.fA, state.fA, state.fB + c, state.fB);
+                          " SkPathDirection::kCW);\n", state.fA, state.fA, state.fB + c, state.fB);
                 }
-                pathStr.appendf("    path.setFillType(SkPath::k%s_FillType);\n", fillTypeStr[f]);
+                pathStr.appendf("    path.setFillType(SkPathFillType::k%s);\n", fillTypeStr[(int)f]);
                 if (b) {
                     pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                          " SkPath::kCW_Direction);\n", state.fC, state.fC, state.fD + d, state.fD);
+                          " SkPathDirection::kCW);\n", state.fC, state.fC, state.fD + d, state.fD);
                 }
                 pathStr.appendf("    testPathOp(reporter, path, pathB, %s, filename);\n",
                         SkPathOpsDebug::OpStr((SkPathOp) op));
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index 0f652c7..38c58bf 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -32,11 +32,11 @@
 
 static void cubicOp1d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 1,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 2,0);
     pathB.close();
@@ -45,11 +45,11 @@
 
 static void cubicOp2d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,1, 1,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
@@ -58,11 +58,11 @@
 
 static void cubicOp3d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,3, 1,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 3,2);
     pathB.close();
@@ -71,11 +71,11 @@
 
 static void cubicOp5d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 1,0, 2,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,2, 1,0, 2,0);
     pathB.close();
@@ -84,11 +84,11 @@
 
 static void cubicOp6d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,6, 1,0, 3,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 6,0);
     pathB.close();
@@ -97,11 +97,11 @@
 
 static void cubicOp7d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 1,0, 3,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 4,3);
     pathB.close();
@@ -110,11 +110,11 @@
 
 static void cubicOp8d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,5, 1,0, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,4, 1,0, 5,0);
     pathB.close();
@@ -123,11 +123,11 @@
 
 static void cubicOp9d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 1,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
@@ -136,12 +136,12 @@
 
 static void quadOp9d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.quadTo(1,6, 1.5f,1);
     path.quadTo(1.5f,0.5f, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.quadTo(1,2, 1.4f,1);
     pathB.quadTo(3,0.4f, 6,1);
@@ -151,14 +151,14 @@
 
 static void lineOp9d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.lineTo(1,6);
     path.lineTo(1.5f,1);
     path.lineTo(1.8f,0.8f);
     path.lineTo(2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.lineTo(1,2);
     pathB.lineTo(1.4f,1);
@@ -170,11 +170,11 @@
 
 static void cubicOp1i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 1,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
@@ -183,11 +183,11 @@
 
 static void cubicOp10d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,3, 1,0, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(1,4, 1,0, 3,1);
     pathB.close();
@@ -196,11 +196,11 @@
 
 static void cubicOp11d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 1,0, 5,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(1,5, 1,0, 4,3);
     pathB.close();
@@ -209,11 +209,11 @@
 
 static void cubicOp12d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 1,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,1, 1,0, 6,1);
     pathB.close();
@@ -222,11 +222,11 @@
 
 static void cubicOp13d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,5, 1,0, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(3,5, 1,0, 5,4);
     pathB.close();
@@ -235,11 +235,11 @@
 
 static void cubicOp14d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 2,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 2,0);
     pathB.close();
@@ -248,11 +248,11 @@
 
 static void cubicOp15d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,6, 2,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 6,3);
     pathB.close();
@@ -261,11 +261,11 @@
 
 static void cubicOp16d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,1, 3,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     pathB.cubicTo(0,1, 2,0, 1,0);
     pathB.close();
@@ -274,11 +274,11 @@
 
 static void cubicOp17d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,2, 4,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 2,0, 2,0);
     pathB.close();
@@ -287,11 +287,11 @@
 
 static void cubicOp18d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,5, 2,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,3);
     pathB.close();
@@ -300,11 +300,11 @@
 
 static void cubicOp19i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,1, 2,1, 6,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(2,6, 2,0, 1,0);
     pathB.close();
@@ -313,11 +313,11 @@
 
 static void cubicOp20d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,1, 6,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,2, 1,0, 1,0);
     pathB.close();
@@ -326,11 +326,11 @@
 
 static void cubicOp21d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,1, 2,1, 6,5);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(5,6, 1,0, 1,0);
     pathB.close();
@@ -339,11 +339,11 @@
 
 static void cubicOp22d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,3, 3,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     pathB.cubicTo(1,2, 1,0, 3,2);
     pathB.close();
@@ -352,11 +352,11 @@
 
 static void cubicOp23d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 4,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(1,2, 1,0, 2,1);
     pathB.close();
@@ -365,11 +365,11 @@
 
 static void cubicOp24d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 2,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(2,3, 1,0, 2,1);
     pathB.close();
@@ -378,156 +378,156 @@
 
 static void testIntersect1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(3, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(3, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(3, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor1(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(3, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(3, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testIntersect2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(0, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kIntersect_SkPathOp, filename);
 }
 
 static void testUnion2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(0, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kUnion_SkPathOp, filename);
 }
 
 static void testDiff2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(0, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kDifference_SkPathOp, filename);
 }
 
 static void testXor2(skiatest::Reporter* reporter, const char* filename) {
     SkPath one, two;
-    one.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    two.addRect(0, 3, 9, 9, SkPath::kCW_Direction);
+    one.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    two.addRect(0, 3, 9, 9, SkPathDirection::kCW);
     testPathOp(reporter, one, two, kXOR_SkPathOp, filename);
 }
 
 static void testOp1d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp3d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(1, 1, 2, 2, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(1, 1, 2, 2, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp1u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 3, 3, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void testOp4d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp5d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    path.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    path.addRect(0, 0, 3, 3, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp6d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 3, 3, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp7d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void testOp2u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    pathB.addRect(1, 1, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 3, 3, SkPathDirection::kCW);
+    pathB.addRect(1, 1, 2, 2, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
@@ -541,11 +541,11 @@
 }
 static void cubicOp25i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,4, 5,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(2,3, 1,0, 4,2);
     pathB.close();
@@ -554,11 +554,11 @@
 
 static void cubicOp26d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 4,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 1,0, 4,3);
     pathB.close();
@@ -567,11 +567,11 @@
 
 static void cubicOp27d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,6, 1,0, 5,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(2,5, 1,0, 6,3);
     pathB.close();
@@ -580,11 +580,11 @@
 
 static void cubicOp28u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,4, 6,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(2,3, 1,0, 4,1);
     pathB.close();
@@ -593,11 +593,11 @@
 
 static void cubicOp29d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,5, 6,0, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(2,4, 1,0, 5,2);
     pathB.close();
@@ -606,11 +606,11 @@
 
 static void cubicOp30d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,5, 6,0, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(3,5, 1,0, 5,2);
     pathB.close();
@@ -619,11 +619,11 @@
 
 static void cubicOp31d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,3, 2,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
@@ -632,11 +632,11 @@
 
 static void cubicOp31u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,3, 2,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
@@ -645,11 +645,11 @@
 
 static void cubicOp31x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,3, 2,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 2,0, 3,0);
     pathB.close();
@@ -658,11 +658,11 @@
 
 static void cubicOp32d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 6,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
@@ -671,11 +671,11 @@
 
 static void cubicOp33i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 6,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 1,0, 2,1);
     pathB.close();
@@ -684,11 +684,11 @@
 
 static void cubicOp34d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,5, 2,1, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(1,3, 1,0, 5,3);
     pathB.close();
@@ -697,11 +697,11 @@
 
 static void cubicOp35d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 2,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 1,0, 5,1);
     pathB.close();
@@ -710,11 +710,11 @@
 
 static void cubicOp36u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 2,0, 5,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,5, 1,0, 6,1);
     pathB.close();
@@ -723,11 +723,11 @@
 
 static void cubicOp37d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,6, 6,1, 4,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,6);
     pathB.cubicTo(3,4, 1,0, 6,2);
     pathB.close();
@@ -736,11 +736,11 @@
 
 static void cubicOp38d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,6, 3,2, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 1,0, 6,0);
     pathB.close();
@@ -749,11 +749,11 @@
 
 static void cubicOp39d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,3, 5,1, 4,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,5);
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.close();
@@ -762,11 +762,11 @@
 
 static void cubicOp40d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 3,2, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(2,4, 1,0, 5,1);
     pathB.close();
@@ -775,11 +775,11 @@
 
 static void cubicOp41i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,6, 4,3, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,4);
     pathB.cubicTo(4,6, 1,0, 6,2);
     pathB.close();
@@ -788,11 +788,11 @@
 
 static void cubicOp42d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 6,5, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 1,0, 2,1);
     pathB.close();
@@ -801,11 +801,11 @@
 
 static void cubicOp43d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(1,2, 4,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(1,3, 2,0, 2,1);
     pathB.close();
@@ -814,11 +814,11 @@
 
 static void cubicOp44d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(3,6, 4,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 6,3);
     pathB.close();
@@ -827,11 +827,11 @@
 
 static void cubicOp45d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(2,4, 4,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(2,3, 2,0, 4,2);
     pathB.close();
@@ -840,11 +840,11 @@
 
 static void cubicOp46d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(3,5, 5,0, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(2,4, 2,0, 5,3);
     pathB.close();
@@ -853,11 +853,11 @@
 
 static void cubicOp47d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 6,2, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,6);
     pathB.cubicTo(4,5, 1,0, 6,1);
     pathB.close();
@@ -866,11 +866,11 @@
 
 static void cubicOp48d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(2,3, 5,1, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,5);
     pathB.cubicTo(2,3, 2,0, 3,2);
     pathB.close();
@@ -879,11 +879,11 @@
 
 static void cubicOp49d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(1,5, 3,2, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(1,4, 2,0, 5,1);
     pathB.close();
@@ -892,11 +892,11 @@
 
 static void cubicOp50d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,3);
     path.cubicTo(1,6, 5,0, 5,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,5, 3,0, 6,1);
     pathB.close();
@@ -905,11 +905,11 @@
 
 static void cubicOp51d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,3);
     path.cubicTo(1,2, 4,1, 6,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,4);
     pathB.cubicTo(0,6, 3,0, 2,1);
     pathB.close();
@@ -918,11 +918,11 @@
 
 static void cubicOp52d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(1,2, 5,4, 4,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,5);
     pathB.cubicTo(3,4, 2,0, 2,1);
     pathB.close();
@@ -931,11 +931,11 @@
 
 static void cubicOp53d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,3);
     path.cubicTo(1,2, 5,3, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,5);
     pathB.cubicTo(1,2, 3,0, 2,1);
     pathB.close();
@@ -944,11 +944,11 @@
 
 static void cubicOp54d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,4);
     path.cubicTo(1,3, 5,4, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,5);
     pathB.cubicTo(2,4, 4,0, 3,1);
     pathB.close();
@@ -957,11 +957,11 @@
 
 static void cubicOp55d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,5);
     path.cubicTo(1,3, 3,2, 5,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(0,5, 5,0, 3,1);
     pathB.close();
@@ -970,11 +970,11 @@
 
 static void cubicOp56d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,6, 5,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,2);
     pathB.close();
@@ -983,11 +983,11 @@
 
 static void cubicOp57d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,5);
     path.cubicTo(0,5, 5,4, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,5);
     pathB.cubicTo(4,6, 5,0, 5,0);
     pathB.close();
@@ -996,11 +996,11 @@
 
 static void cubicOp58d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,5);
     path.cubicTo(3,4, 6,5, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(5,6);
     pathB.cubicTo(3,5, 5,0, 4,3);
     pathB.close();
@@ -1009,11 +1009,11 @@
 
 static void cubicOp59d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(5,6, 4,0, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(1,4, 1,0, 6,5);
     pathB.close();
@@ -1022,11 +1022,11 @@
 
 static void cubicOp60d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(4,6, 6,0, 5,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(2,5, 2,0, 6,4);
     pathB.close();
@@ -1035,11 +1035,11 @@
 
 static void cubicOp61d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1,2);
     path.cubicTo(0,5, 3,2, 6,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(1,6, 2,1, 5,0);
     pathB.close();
@@ -1048,11 +1048,11 @@
 
 static void cubicOp62d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1,3);
     path.cubicTo(5,6, 5,3, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,5);
     pathB.cubicTo(4,5, 3,1, 6,5);
     pathB.close();
@@ -1061,11 +1061,11 @@
 
 static void cubicOp63d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2,3);
     path.cubicTo(0,4, 3,2, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(3,5, 3,2, 4,0);
     pathB.close();
@@ -1113,11 +1113,11 @@
 
 static void cubicOp66u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,6, 4,2, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,4);
     pathB.cubicTo(3,5, 1,0, 6,2);
     pathB.close();
@@ -1194,7 +1194,7 @@
     rects[4].setLTRB(xC, yC, xF, yF);
     paths[4].addRoundRect(rects[4], SkIntToScalar(5), SkIntToScalar(5));  // cyan
     SkPath path;
-    path.setFillType(SkPath::kInverseEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kInverseEvenOdd);
     for (int index = 0; index < 5; ++index) {
         SkString uniqueName;
         uniqueName.printf("%s%d", filename, index);
@@ -1205,7 +1205,7 @@
 
 static void skp1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(189,7);
     path.cubicTo(189,5.34314585f, 190.34314f,4, 192,4);
     path.lineTo(243,4);
@@ -1227,7 +1227,7 @@
     path.lineTo(191,8);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(189,4);
     pathB.lineTo(199,14);
     pathB.lineTo(236,14);
@@ -1239,7 +1239,7 @@
 
 static void skp2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(253.000000f, 11757.0000f);
     path.lineTo(253.000000f, 222.000000f);
     path.lineTo(823.000000f, 222.000000f);
@@ -1247,7 +1247,7 @@
     path.lineTo(253.000000f, 11757.0000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(258.000000f, 1028.00000f);
     pathB.lineTo(258.000000f, 1027.00000f);
     pathB.lineTo(823.000000f, 1027.00000f);
@@ -1259,7 +1259,7 @@
 
 static void skp3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(717.000000f, 507.000000f);
     path.lineTo(717.000000f, 425.000000f);
     path.lineTo(973.000000f, 425.000000f);
@@ -1279,7 +1279,7 @@
     path.lineTo(719.000000f, 426.000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(717.000000f, 510.000000f);
     pathB.lineTo(760.000000f, 467.000000f);
     pathB.lineTo(930.000000f, 467.000000f);
@@ -1291,7 +1291,7 @@
 
 static void skp4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(230.756805f, 591.756775f);
     path.quadTo(232.514725f, 590.000000f, 235.000000f, 590.000000f);
     path.lineTo(300.000000f, 590.000000f);
@@ -1311,7 +1311,7 @@
     path.lineTo(231.000000f, 597.000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(306.000000f, 590.000000f);
     pathB.lineTo(292.000000f, 604.000000f);
     pathB.lineTo(305.000000f, 617.000000f);
@@ -1323,7 +1323,7 @@
 
 static void skp5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(18.0000000f, 226.000000f);
     path.quadTo(14.6862917f, 226.000000f, 12.3423996f, 228.342407f);
     path.quadTo(10.0000000f, 230.686295f, 10.0000000f, 234.000000f);
@@ -1335,7 +1335,7 @@
     path.lineTo(18.0000000f, 226.000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(18.0000000f, 226.000000f);
     pathB.lineTo(1239.00000f, 226.000000f);
     pathB.cubicTo(1243.41833f, 226.000000f, 1247.00000f, 229.581726f, 1247.00000f, 234.000000f);
@@ -1349,11 +1349,11 @@
 
 static void cubicOp70d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,5, 4,0, 5,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(0,5, 1,0, 5,0);
     pathB.close();
@@ -1362,11 +1362,11 @@
 
 static void cubicOp71d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,5, 4,1, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,4);
     pathB.cubicTo(4,6, 1,0, 5,0);
     pathB.close();
@@ -1375,11 +1375,11 @@
 
 static void cubicOp72i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,5, 5,2, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,5);
     pathB.cubicTo(4,5, 1,0, 5,0);
     pathB.close();
@@ -1388,12 +1388,12 @@
 
 static void cubicOp73d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 4,0, 6,4);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(4,6, 1,0, 4,3);
     pathB.lineTo(0,4);
@@ -1403,12 +1403,12 @@
 
 static void cubicOp74d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 5,1, 5,1);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,5);
     pathB.cubicTo(1,5, 1,0, 5,1);
     pathB.lineTo(1,5);
@@ -1418,12 +1418,12 @@
 
 static void cubicOp75d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,4, 5,1, 6,4);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,5);
     pathB.cubicTo(4,6, 1,0, 4,0);
     pathB.lineTo(1,5);
@@ -1433,11 +1433,11 @@
 
 static void cubicOp76u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 2,0, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(3,5, 1,0, 2,0);
     pathB.close();
@@ -1446,12 +1446,12 @@
 
 static void cubicOp77i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,1);
     path.cubicTo(1,3, 2,0, 3,2);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(0,2);
     pathB.cubicTo(2,3, 1,0, 3,1);
     pathB.lineTo(0,2);
@@ -1461,12 +1461,12 @@
 
 static void cubicOp78u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1,6);
     path.cubicTo(1,6, 5,0, 6,1);
     path.lineTo(1,6);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,6, 6,1, 6,1);
     pathB.lineTo(0,5);
@@ -1476,11 +1476,11 @@
 
 static void cubicOp79u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,3, 1,0, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(4,6, 1,0, 3,1);
     pathB.close();
@@ -1489,12 +1489,12 @@
 
 static void cubicOp80i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,3, 2,1, 4,3);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(3,4, 1,0, 3,2);
     pathB.lineTo(1,2);
@@ -1504,11 +1504,11 @@
 
 static void cubicOp81d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,6, 4,3, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,4);
     pathB.cubicTo(4,5, 1,0, 6,4);
     pathB.close();
@@ -1517,12 +1517,12 @@
 
 static void cubicOp82i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,1);
     path.cubicTo(2,3, 5,2, 3,0);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,5);
     pathB.cubicTo(0,3, 1,0, 3,2);
     pathB.lineTo(2,5);
@@ -1532,12 +1532,12 @@
 
 static void cubicOp83i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,3, 2,1, 4,1);
     path.lineTo(0,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(1,4, 1,0, 3,0);
     pathB.lineTo(1,2);
@@ -1547,11 +1547,11 @@
 
 static void cubicOp84d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,4);
     path.cubicTo(2,3, 6,3, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,6);
     pathB.cubicTo(2,3, 4,0, 3,2);
     pathB.close();
@@ -1560,7 +1560,7 @@
 
 static void skpClip1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1126.17114f, 877.171204f);
     path.quadTo(1127.34314f, 876.000000f, 1129.00000f, 876.000000f);
     path.lineTo(1243.00000f, 876.000000f);
@@ -1578,7 +1578,7 @@
     path.quadTo(1125.00000f, 878.343140f, 1126.17114f, 877.171204f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1247.00000f, 876.000000f);
     pathB.lineTo(1231.00000f, 892.000000f);
     pathB.lineTo(1246.00000f, 907.000000f);
@@ -1590,7 +1590,7 @@
 
 static void skpClip2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(134.000000f, 11414.0000f);
     path.cubicTo(131.990234f, 11414.0000f, 130.326660f, 11415.4824f, 130.042755f, 11417.4131f);
     path.cubicTo(130.233124f, 11418.3193f, 131.037079f, 11419.0000f, 132.000000f, 11419.0000f);
@@ -1600,7 +1600,7 @@
     path.lineTo(134.000000f, 11414.0000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(132.000000f, 11415.0000f);
     pathB.lineTo(806.000000f, 11415.0000f);
     pathB.cubicTo(807.104553f, 11415.0000f, 808.000000f, 11415.4473f, 808.000000f, 11416.0000f);
@@ -1616,7 +1616,7 @@
 
 static void skp96prezzi1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(157.464005f, 670.463989f);
     path.quadTo(158.928925f, 669.000000f, 161.000000f, 669.000000f);
     path.lineTo(248.000000f, 669.000000f);
@@ -1634,7 +1634,7 @@
     path.quadTo(156.000000f, 671.928955f, 157.464005f, 670.463989f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(156.000000f, 669.000000f);
     pathB.lineTo(178.500000f, 691.500000f);
     pathB.lineTo(230.500000f, 691.500000f);
@@ -1646,7 +1646,7 @@
 
 static void skpancestry_com1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(161.000000f, 925.000000f);
     path.cubicTo(159.874390f, 925.000000f, 158.835663f, 925.371948f, 158.000000f, 925.999634f);
     path.lineTo(158.000000f, 926.000000f);
@@ -1656,7 +1656,7 @@
     path.lineTo(161.000000f, 925.000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(161.000000f, 926.000000f);
     pathB.lineTo(1105.00000f, 926.000000f);
     pathB.cubicTo(1107.20911f, 926.000000f, 1109.00000f, 927.790833f, 1109.00000f, 930.000000f);
@@ -1672,7 +1672,7 @@
 
 static void skpeldorado_com_ua1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(286.695129f, 291.000000f);
     path.lineTo(229.304855f, 561.000000f);
     path.lineTo(979.304871f, 561.000000f);
@@ -1680,7 +1680,7 @@
     path.lineTo(286.695129f, 291.000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1006.69513f, 291.000000f);
     pathB.cubicTo(1023.26367f, 291.000000f, 1033.84021f, 304.431458f, 1030.31836f, 321.000000f);
     pathB.lineTo(985.681519f, 531.000000f);
@@ -1696,7 +1696,7 @@
 
 static void skpbyte_com1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(968.000000f, 14.0000000f);
     path.cubicTo(965.238586f, 14.0000000f, 963.000000f, 16.2385769f, 963.000000f, 19.0000000f);
     path.lineTo(963.000000f, 32.0000000f);
@@ -1708,7 +1708,7 @@
     path.lineTo(968.000000f, 14.0000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(968.000000f, 14.0000000f);
     pathB.lineTo(1034.00000f, 14.0000000f);
     pathB.cubicTo(1036.76147f, 14.0000000f, 1039.00000f, 16.2385750f, 1039.00000f, 19.0000000f);
@@ -1724,7 +1724,7 @@
 
 static void skphealth_com76(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(708.099182f, 7.09919119f);
     path.lineTo(708.099182f, 7.09920025f);
     path.quadTo(704.000000f, 11.2010098f, 704.000000f, 17.0000000f);
@@ -1735,7 +1735,7 @@
     path.lineTo(708.099182f, 7.09919119f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(704.000000f, 3.00000000f);
     pathB.lineTo(704.000000f, 33.0000000f);
     pathB.lineTo(705.000000f, 33.0000000f);
@@ -1745,7 +1745,7 @@
 
 static void skpahrefs_com88(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1099.82886f, 7.17117119f);
     path.lineTo(1099.12134f, 7.87867832f);
     path.cubicTo(1099.66418f, 8.42157173f, 1100.00000f, 9.17157173f, 1100.00000f, 10.0000000f);
@@ -1761,7 +1761,7 @@
     path.lineTo(1099.82886f, 7.17117119f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1101.00000f, 6.00000000f);
     pathB.lineTo(1088.00000f, 6.00000000f);
     pathB.lineTo(1088.00000f, 19.0000000f);
@@ -1771,7 +1771,7 @@
 
 static void skpahrefs_com29(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1037.17114f, 7.17119980f);
     path.quadTo(1038.34314f, 6.00000000f, 1040.00000f, 6.00000000f);
     path.lineTo(1074.00000f, 6.00000000f);
@@ -1791,7 +1791,7 @@
     path.lineTo(1037.00000f, 10.0000000f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1036.00000f, 32.0000000f);
     pathB.lineTo(1049.00000f, 19.0000000f);
     pathB.lineTo(1073.00000f, 31.0000000f);
@@ -1801,12 +1801,12 @@
 
 static void cubicOp85d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 1,0, 6,2);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(2,6, 1,0, 6,1);
     pathB.close();
@@ -1815,7 +1815,7 @@
 
 static void skpkkiste_to98(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(96, 122);
     path.cubicTo(94.6192932f, 122, 93.3692932f, 122.559647f, 92.4644699f, 123.46447f);
     path.lineTo(94.1715698f, 125.17157f);
@@ -1827,7 +1827,7 @@
     path.lineTo(96, 122);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(258, 122);
     pathB.cubicTo(260.761414f, 122, 263, 124.238579f, 263, 127);
     pathB.lineTo(263, 284);
@@ -1981,11 +1981,11 @@
 
 static void cubicOp85i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(1, 5, 4, 3, 6, 4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 4);
     pathB.cubicTo(4, 6, 4, 3, 5, 1);
     pathB.close();
@@ -2000,7 +2000,7 @@
     path1.lineTo(0, 1);
     path1.lineTo(0, 0);
     path1.close();
-    path1.setFillType(SkPath::kWinding_FillType);
+    path1.setFillType(SkPathFillType::kWinding);
     SkPath path2;
     path2.moveTo(0.646446645f, -0.353553414f);
     path2.quadTo(0.792893291f, -0.50000006f, 1.00000012f, -0.50000006f);
@@ -2018,35 +2018,35 @@
     path2.quadTo(0.50000006f, 0.792893291f, 0.646446645f, 0.646446645f);
     path2.quadTo(0.792893291f, 0.50000006f, 1.00000012f, 0.50000006f);
     path2.close();
-    path2.setFillType(SkPath::kEvenOdd_FillType);
+    path2.setFillType(SkPathFillType::kEvenOdd);
     testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp1i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp2i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 3, 3, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rectOp3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 0);
     path.lineTo(3, 3);
@@ -2057,7 +2057,7 @@
     path.lineTo(3, 3);
     path.lineTo(2, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 1);
     pathB.lineTo(3, 1);
     pathB.lineTo(3, 3);
@@ -2080,7 +2080,7 @@
     path1.lineTo(90, 230);
     path1.lineTo(160, 60);
     path1.close();
-    path1.setFillType(SkPath::kEvenOdd_FillType);
+    path1.setFillType(SkPathFillType::kEvenOdd);
 
     SkPath path2;
     path2.moveTo(142.589081f, 102.283646f);
@@ -2118,13 +2118,13 @@
     path2.lineTo(181.176468f, 120);
     path2.lineTo(195.830978f, 161.521133f);
     path2.close();
-    path2.setFillType(SkPath::kEvenOdd_FillType);
+    path2.setFillType(SkPathFillType::kEvenOdd);
     testPathOp(reporter, path1, path2, kIntersect_SkPathOp, filename);
 }
 
 static void skpkkiste_to716(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1173, 284);
     path.cubicTo(1173, 285.125824f, 1173.37207f, 286.164734f, 1174, 287.000488f);
     path.lineTo(1174, 123.999496f);
@@ -2132,7 +2132,7 @@
     path.lineTo(1173, 284);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1340, 122);
     pathB.cubicTo(1342.76147f, 122, 1345, 124.238579f, 1345, 127);
     pathB.lineTo(1345, 284);
@@ -2148,7 +2148,7 @@
 
 static void loopEdge1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(3,0);
     path.lineTo(3,2);
@@ -2159,7 +2159,7 @@
     path.lineTo(0,3);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(1,2);
     pathB.lineTo(2,2);
     pathB.lineTo(2,4);
@@ -2170,7 +2170,7 @@
 
 static void loopEdge2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(3,0);
     path.lineTo(3,2);
@@ -2181,7 +2181,7 @@
     path.lineTo(0,3);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(1 - 1e-6f,2);
     pathB.lineTo(2 - 1e-6f,2);
     pathB.lineTo(2 - 1e-6f,4);
@@ -2192,11 +2192,11 @@
 
 static void cubicOp86i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 4);
     path.cubicTo(3, 4, 6, 2, 5, 2);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(2, 6);
     pathB.cubicTo(2, 5, 4, 0, 4, 3);
     pathB.close();
@@ -2205,11 +2205,11 @@
 
 static void cubicOp87u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 2,0, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(4,6, 1,0, 2,0);
     pathB.close();
@@ -2219,11 +2219,11 @@
 
 static void cubicOp88u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,5, 5,0, 6,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(4,6, 1,0, 5,2);
     pathB.close();
@@ -2232,11 +2232,11 @@
 
 static void cubicOp89u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 3);
     path.cubicTo(1, 6, 5, 0, 6, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 6, 3, 0, 6, 1);
     pathB.close();
@@ -2245,11 +2245,11 @@
 
 static void cubicOp90u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 5);
     path.cubicTo(1, 2, 5, 2, 4, 1);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(2, 5);
     pathB.cubicTo(1, 4, 5, 0, 2, 1);
     pathB.close();
@@ -2258,11 +2258,11 @@
 
 static void cubicOp91u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 6);
     path.cubicTo(0, 3, 6, 3, 5, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
@@ -2271,7 +2271,7 @@
 
 static void skpaaalgarve_org53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
    path.moveTo(-1.24344979e-014f, 348);
     path.lineTo(258, 348);
     path.lineTo(258, 322);
@@ -2281,7 +2281,7 @@
     path.lineTo(-1.24344979e-014f, 348);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
    pathB.moveTo(0, 312);
     pathB.lineTo(258, 312);
     pathB.lineTo(258, 348);
@@ -2292,7 +2292,7 @@
 
 static void skpabcspark_ca103(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1.99840144e-015f, 494);
     path.lineTo(97, 494);
     path.quadTo(100.313705f, 494, 102.6576f, 491.657593f);
@@ -2304,7 +2304,7 @@
     path.lineTo(1.99840144e-015f, 494);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 417);
     pathB.lineTo(105, 417);
     pathB.lineTo(105, 494);
@@ -2315,7 +2315,7 @@
 
 static void skpacesoftech_com47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(670.537415f, 285);
     path.lineTo(670.387451f, 285);
     path.lineTo(596.315186f, 314.850708f);
@@ -2325,7 +2325,7 @@
     path.lineTo(670.537415f, 285);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(663.318542f, 374.100616f);
     pathB.quadTo(647.950989f, 380.293671f, 632.705322f, 373.806305f);
     pathB.quadTo(617.459595f, 367.318909f, 611.266541f, 351.951355f);
@@ -2341,7 +2341,7 @@
 
 static void skpact_com43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1.45716772e-016f, 924.336121f);
     path.lineTo(-1.11022302e-016f, 920);
     path.lineTo(6, 920);
@@ -2356,7 +2356,7 @@
     path.cubicTo(2.79086018f, 925, 1, 923.209167f, 1, 921);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(-1, 920);
     pathB.lineTo(0, 920);
     pathB.lineTo(3, 927);
@@ -2366,7 +2366,7 @@
 
 static void skpadbox_lt8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(320.097229f, 628.573669f);
     path.lineTo(610.227173f, 85.7786865f);
     path.lineTo(946.652588f, 265.601807f);
@@ -2374,7 +2374,7 @@
     path.lineTo(320.097229f, 628.573669f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(333.866608f, 623.496155f);
     pathB.lineTo(613.368042f, 100.585754f);
     pathB.cubicTo(613.685303f, 99.9921265f, 614.423767f, 99.7681885f, 615.017395f, 100.085449f);
@@ -2390,7 +2390,7 @@
 
 static void skpadindex_de4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 926);
     path.lineTo(0, 0);
     path.lineTo(1280, 0);
@@ -2398,7 +2398,7 @@
     path.lineTo(0, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 312);
     pathB.lineTo(8.20486257e-015f, 178);
     pathB.lineTo(49, 178);
@@ -2409,7 +2409,7 @@
 
 static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(205.605804f, 142.334625f);
     path.lineTo(254.665359f, 85.6058044f);
     path.lineTo(311.394196f, 134.665359f);
@@ -2417,7 +2417,7 @@
     path.lineTo(205.605804f, 142.334625f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(283.407959f, 110.462646f);
     pathB.cubicTo(298.864319f, 123.829437f, 300.558258f, 147.195221f, 287.191467f, 162.651581f);
     pathB.lineTo(286.537354f, 163.407959f);
@@ -2433,7 +2433,7 @@
 
 static void skpadspert_de11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(-4.4408921e-016f, 682.5f);
     path.lineTo(30.5f, 682.5f);
     path.cubicTo(32.709137f, 682.5f, 34.5f, 680.709167f, 34.5f, 678.5f);
@@ -2443,7 +2443,7 @@
     path.lineTo(-4.4408921e-016f, 682.5f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 482);
     pathB.lineTo(35, 482);
     pathB.lineTo(35, 683);
@@ -2454,7 +2454,7 @@
 
 static void skpaiaigames_com870(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(324.071075f, 845.071045f);
     path.cubicTo(324.405151f, 844.737f, 324.715668f, 844.379395f, 325, 844.000977f);
     path.lineTo(325, 842.127197f);
@@ -2468,7 +2468,7 @@
     path.cubicTo(324.571411f, 716.043762f, 324.017761f, 715.289856f, 323.363953f, 714.636047f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(317, 711);
     pathB.cubicTo(322.522858f, 711, 327, 715.477173f, 327, 721);
     pathB.lineTo(327, 838);
@@ -2484,11 +2484,11 @@
 
 static void cubicOp92i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(2, 6, 4, 1, 5, 4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(4, 5, 1, 0, 6, 2);
     pathB.close();
@@ -2497,11 +2497,11 @@
 
 static void cubicOp93d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(1, 6, 4, 1, 4, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 4, 1, 0, 6, 1);
     pathB.close();
@@ -2510,11 +2510,11 @@
 
 static void cubicOp94u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 3);
     path.cubicTo(2, 3, 5, 0, 5, 3);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 5, 3, 0, 3, 2);
     pathB.close();
@@ -2523,7 +2523,7 @@
 
 static void skpadbox_lt15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(333.292084f, 624.570984f);
     path.lineTo(614.229797f, 98.9735107f);
     path.lineTo(933.457764f, 269.604431f);
@@ -2531,7 +2531,7 @@
     path.lineTo(333.292084f, 624.570984f);
     path.close();
     SkPath pathB;
-     pathB.setFillType(SkPath::kWinding_FillType);
+     pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(613.368042f, 100.585754f);
     pathB.cubicTo(613.685303f, 99.9921265f, 614.423767f, 99.7681885f, 615.017395f, 100.085449f);
     pathB.lineTo(932.633057f, 269.854553f);
@@ -2547,7 +2547,7 @@
 
 static void skpadoption_org196(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(802, 367);
     path.lineTo(802, 324);
     path.lineTo(956, 324);
@@ -2558,7 +2558,7 @@
     path.cubicTo(806.029419f, 376, 802, 371.970551f, 802, 367);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(803, 326);
     pathB.lineTo(955, 326);
     pathB.lineTo(955, 370);
@@ -2572,7 +2572,7 @@
 
 static void skpadspert_net23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(-2.220446e-018f, 483.5f);
     path.lineTo(0, 482.5f);
     path.lineTo(30.5f, 482.5f);
@@ -2588,7 +2588,7 @@
     path.lineTo(-2.220446e-018f, 483.5f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 482);
     pathB.lineTo(35, 482);
     pathB.lineTo(35, 683);
@@ -2599,7 +2599,7 @@
 
 static void skpadventistmission_org572(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1182.00037f, 926);
     path.cubicTo(1181.08813f, 924.785583f, 1179.63586f, 924, 1178, 924);
     path.lineTo(938, 924);
@@ -2607,7 +2607,7 @@
     path.lineTo(1182.00037f, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(934, 924);
     pathB.lineTo(1182, 924);
     pathB.lineTo(1182, 926);
@@ -2618,7 +2618,7 @@
 
 static void skpagentxsites_com55(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(925, 27);
     path.cubicTo(924.447693f, 27, 924, 27.4477158f, 924, 28);
     path.lineTo(924, 55);
@@ -2630,7 +2630,7 @@
     path.lineTo(925, 27);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1103, 27);
     pathB.cubicTo(1104.10461f, 27, 1105, 27.8954315f, 1105, 29);
     pathB.lineTo(1105, 54);
@@ -2646,7 +2646,7 @@
 
 static void skpbakosoft_com10(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(190, 170);
     path.cubicTo(178.9543f, 170, 170, 178.9543f, 170, 190);
     path.cubicTo(170, 201.0457f, 178.9543f, 210, 190, 210);
@@ -2656,7 +2656,7 @@
     path.lineTo(190, 170);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(210, 190);
     pathB.quadTo(210, 198.284271f, 204.142136f, 204.142136f);
     pathB.quadTo(198.284271f, 210, 190, 210);
@@ -2672,7 +2672,7 @@
 
 static void skpbambootheme_com12(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(47.8780937f, 58);
     path.lineTo(0, 58);
     path.lineTo(-8.65973959e-015f, 96.9914017f);
@@ -2680,7 +2680,7 @@
     path.quadTo(44.9466133f, 71.764061f, 47.8780937f, 58);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(-1, -3);
     pathB.lineTo(-1, -3);
     pathB.cubicTo(26.6142502f, -3, 49, 19.3857498f, 49, 47);
@@ -2696,7 +2696,7 @@
 
 static void skpakmmos_ru100(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(693.000488f, 926);
     path.cubicTo(692.164734f, 925.37207f, 691.125793f, 925, 690, 925);
     path.lineTo(578, 925);
@@ -2704,7 +2704,7 @@
     path.lineTo(693.000488f, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(575, 925);
     pathB.lineTo(693, 925);
     pathB.lineTo(693, 926);
@@ -2715,7 +2715,7 @@
 
 static void skpcarpetplanet_ru22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(195, 785);
     path.cubicTo(124.307556f, 785, 67, 841.859863f, 67, 912);
     path.lineTo(67, 913);
@@ -2725,7 +2725,7 @@
     path.cubicTo(314.09201f, 833.437622f, 260.247131f, 785, 195, 785);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(195, 785);
     pathB.cubicTo(265.140167f, 785, 322, 842.307556f, 322, 913);
     pathB.cubicTo(322, 983.692444f, 265.140167f, 1041, 195, 1041);
@@ -2739,7 +2739,7 @@
 
 static void skpcarrot_is24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(945, 597);
     path.quadTo(913.93396f, 597, 891.96698f, 618.96698f);
     path.quadTo(870, 640.93396f, 870, 672);
@@ -2751,7 +2751,7 @@
     path.quadTo(976.06604f, 597, 945, 597);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(945.080994f, 597.161987f);
     pathB.cubicTo(903.659973f, 597.161987f, 870.080994f, 630.73999f, 870.080994f, 672.161987f);
     pathB.cubicTo(870.080994f, 676.096008f, 870.387024f, 679.957031f, 870.971008f, 683.726013f);
@@ -2765,7 +2765,7 @@
 
 static void skpbangalorenest_com4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 926);
     path.lineTo(0, 0);
     path.lineTo(1265, 0);
@@ -2773,7 +2773,7 @@
     path.lineTo(0, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 290);
     pathB.lineTo(-2.64514972e-014f, 146);
     pathB.lineTo(30, 146);
@@ -2784,7 +2784,7 @@
 
 static void skpbenzoteh_ru152(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(883, 23);
     path.lineTo(883, 0);
     path.lineTo(1122.5f, 0);
@@ -2796,7 +2796,7 @@
     path.quadTo(883, 25.0710678f, 883, 23);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(883, 0);
     pathB.lineTo(1123, 0);
     pathB.lineTo(1123, 23);
@@ -2811,7 +2811,7 @@
 
 static void skpbestred_ru37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(883, 23);
     path.lineTo(883, 0);
     path.lineTo(1122.5f, 0);
@@ -2823,7 +2823,7 @@
     path.quadTo(883, 25.0710678f, 883, 23);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(883, 0);
     pathB.lineTo(1123, 0);
     pathB.lineTo(1123, 23);
@@ -2838,7 +2838,7 @@
 
 static void skpbingoentertainment_net189(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(896, 745.38678f);
     path.lineTo(896, 873.38678f);
     path.lineTo(922.567993f, 876.683716f);
@@ -2846,7 +2846,7 @@
     path.lineTo(896, 745.38678f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(899.200928f, 745.783997f);
     pathB.cubicTo(897.119385f, 745.525696f, 895.432007f, 752.031982f, 895.432007f, 760.316284f);
     pathB.lineTo(895.432007f, 858.316284f);
@@ -2862,7 +2862,7 @@
 
 static void skpcarrefour_ro62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1104, 453);
     path.lineTo(399, 453);
     path.lineTo(399, 657);
@@ -2872,7 +2872,7 @@
     path.lineTo(1104, 453);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(400, 453);
     pathB.lineTo(1103, 453);
     pathB.lineTo(1103, 666);
@@ -2885,7 +2885,7 @@
 
 static void skpcaffelavazzait_com_ua21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(883, 23);
     path.lineTo(883, 0);
     path.lineTo(1122.5f, 0);
@@ -2897,7 +2897,7 @@
     path.quadTo(883, 25.0710678f, 883, 23);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(883, 0);
     pathB.lineTo(1123, 0);
     pathB.lineTo(1123, 23);
@@ -2912,7 +2912,7 @@
 
 static void skpcamcorder_kz21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(883, 23);
     path.lineTo(883, 0);
     path.lineTo(1122.5f, 0);
@@ -2924,7 +2924,7 @@
     path.quadTo(883, 25.0710678f, 883, 23);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(883, 0);
     pathB.lineTo(1123, 0);
     pathB.lineTo(1123, 23);
@@ -2939,7 +2939,7 @@
 
 static void skpcavablar_net563(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(160.000488f, 918);
     path.cubicTo(159.164749f, 917.37207f, 158.125824f, 917, 157, 917);
     path.lineTo(94, 917);
@@ -2947,7 +2947,7 @@
     path.lineTo(160.000488f, 918);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(91, 917);
     pathB.lineTo(160, 917);
     pathB.lineTo(160, 918);
@@ -2958,7 +2958,7 @@
 
 static void skpinsomnia_gr72(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1138, 231);
     path.lineTo(1137, 243.625748f);
     path.lineTo(1137, 926);
@@ -2967,7 +2967,7 @@
     path.lineTo(1138, 231);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1139, 231);
     pathB.lineTo(1138, 231);
     pathB.lineTo(633, 6101);
@@ -2977,11 +2977,11 @@
 
 static void cubicOp95u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 2);
     path.cubicTo(2, 3, 5, 1, 3, 2);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(1, 5);
     pathB.cubicTo(2, 3, 2, 0, 3, 2);
     pathB.close();
@@ -2990,11 +2990,11 @@
 
 static void cubicOp96d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 6);
     path.cubicTo(0, 3, 6, 3, 5, 0);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(3, 6);
     pathB.cubicTo(0, 5, 6, 1, 3, 0);
     pathB.close();
@@ -3003,11 +3003,11 @@
 
 static void cubicOp97x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 2);
     path.cubicTo(0, 6, 2, 1, 2, 1);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(1, 2);
     pathB.cubicTo(1, 2, 2, 0, 6, 0);
     pathB.close();
@@ -3016,11 +3016,11 @@
 
 static void cubicOp98x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 3);
     path.cubicTo(3, 6, 4, 1, 6, 3);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(1, 4);
     pathB.cubicTo(3, 6, 3, 0, 6, 3);
     pathB.close();
@@ -3029,11 +3029,11 @@
 
 static void cubicOp99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3,6);
     path.cubicTo(0,3, 6,5, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(5,6);
     pathB.cubicTo(4,5, 6,3, 3,0);
     pathB.close();
@@ -3042,11 +3042,11 @@
 
 static void cubicOp100(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,2, 2,1, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(2,4, 1,0, 2,0);
     pathB.close();
@@ -3055,11 +3055,11 @@
 
 static void cubicOp101(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(2, 3, 2, 1, 5, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(3, 5, 1, 0, 3, 2);
     pathB.close();
@@ -3068,11 +3068,11 @@
 
 static void cubicOp102(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,2, 1,0, 3,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,1);
     pathB.cubicTo(0,3, 1,0, 2,1);
     pathB.close();
@@ -3081,11 +3081,11 @@
 
 static void cubicOp103(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 2,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 1,0, 5,1);
     pathB.close();
@@ -3094,11 +3094,11 @@
 
 static void cubicOp104(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,6, 4,0, 6,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,4);
     pathB.cubicTo(1,6, 1,0, 6,0);
     pathB.close();
@@ -3107,11 +3107,11 @@
 
 static void cubicOp105(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,4, 6,5, 2,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(5,6);
     pathB.cubicTo(0,2, 1,0, 4,0);
     pathB.close();
@@ -3120,11 +3120,11 @@
 
 static void cubicOp106(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(4, 6, 2, 1, 2, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
@@ -3133,11 +3133,11 @@
 
 static void cubicOp107(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(4, 6, 2, 1, 2, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
@@ -3146,11 +3146,11 @@
 
 static void cubicOp108(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(4, 6, 2, 1, 2, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(0, 2, 1, 0, 6, 4);
     pathB.close();
@@ -3159,11 +3159,11 @@
 
 static void cubicOp109(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,5, 6,3, 5,4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3,6);
     pathB.cubicTo(4,5, 1,0, 5,4);
     pathB.close();
@@ -3172,22 +3172,22 @@
 
 static void cubicOp110(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 4, 4, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 4, 4, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void cubicOp111(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1,4);
     path.cubicTo(0,5, 4,1, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
@@ -3196,11 +3196,11 @@
 
 static void xOp1u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 4);
     path.cubicTo(4, 5, 3, 2, 6, 3);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(2, 3);
     pathB.cubicTo(3, 6, 4, 1, 5, 4);
     pathB.close();
@@ -3209,11 +3209,11 @@
 
 static void xOp1i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 4);
     path.cubicTo(1, 5, 6, 0, 5, 1);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(0, 6);
     pathB.cubicTo(1, 5, 4, 1, 5, 1);
     pathB.close();
@@ -3222,11 +3222,11 @@
 
 static void xOp2i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 5);
     path.cubicTo(0, 4, 3, 2, 6, 1);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(2, 3);
     pathB.cubicTo(1, 6, 5, 1, 4, 0);
     pathB.close();
@@ -3235,11 +3235,11 @@
 
 static void xOp3i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1,4);
     path.cubicTo(0,5, 4,1, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,4);
     pathB.cubicTo(1,3, 4,1, 5,0);
     pathB.close();
@@ -3248,11 +3248,11 @@
 
 static void findFirst1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,6, 5,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,2, 1,0, 6,1);
     pathB.close();
@@ -3261,11 +3261,11 @@
 
 static void cubicOp112(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2,4);
     path.cubicTo(2,3, 6,4, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,6);
     pathB.cubicTo(0,1, 4,2, 3,2);
     pathB.close();
@@ -3285,11 +3285,11 @@
 
 static void cubicOp114(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(1, 3, -1, 2, 3.5f, 1.33333337f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
     pathB.close();
@@ -3298,11 +3298,11 @@
 
 static void cubicOp114asQuad(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(1, 3, -1, 2, 3.5f, 1.33333337f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(-1, 2, 3.5f, 1.33333337f, 0, 1);
     pathB.close();
@@ -3384,7 +3384,7 @@
 
 static void rects1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(1, 0);
     path.lineTo(1, 1);
@@ -3395,7 +3395,7 @@
     path.lineTo(6, 6);
     path.lineTo(0, 6);
     path.close();
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(0, 0);
     pathB.lineTo(1, 0);
     pathB.lineTo(1, 1);
@@ -3411,7 +3411,7 @@
 
 static void rects2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(4, 0);
     path.lineTo(4, 4);
@@ -3422,7 +3422,7 @@
     path.lineTo(4, 4);
     path.lineTo(3, 4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 3);
     pathB.lineTo(6, 3);
     pathB.lineTo(6, 6);
@@ -3438,23 +3438,23 @@
 
 static void rects3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 4, 4, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 4, 4, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void rects4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 3, 3, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 3, 3, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
@@ -3508,11 +3508,11 @@
 
 static void cubicOp115(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 2,1, 5,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(3,5, 1,0, 4,3);
     pathB.close();
@@ -3522,31 +3522,31 @@
 
 static void testRect1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, path2;
-    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
-    path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-//    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCCW);
+    path.addRect(30, 20, 50, 50, SkPathDirection::kCCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+//    path.addRect(32, 24, 36, 41, SkPathDirection::kCCW);
     testPathOp(reporter, path, path2, kUnion_SkPathOp, filename);
 }
 
 static void testRect2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(4, 4, 5, 5, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 2, 2, SkPath::kCW_Direction);
-    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(4, 4, 5, 5, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 2, 2, SkPathDirection::kCW);
+    pathB.addRect(0, 0, 6, 6, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void cubicOp116(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,6, 2,0, 2,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(0,2, 1,0, 6,4);
     pathB.close();
@@ -3555,11 +3555,11 @@
 
 static void cubicOp117(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,5, 6,0, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(0,1, 1,0, 5,4);
     pathB.close();
@@ -3568,11 +3568,11 @@
 
 static void cubicOp118(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(4,6, 5,1, 6,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,5);
     pathB.cubicTo(2,6, 1,0, 6,4);
     pathB.close();
@@ -3671,11 +3671,11 @@
 
 static void cubicOp119(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,5, 2,1, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(1,3, 1,0, 5,3);
     pathB.close();
@@ -3684,11 +3684,11 @@
 
 static void cubicOp120(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,4, 2,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,4, 1,0, 4,2);
     pathB.close();
@@ -3697,11 +3697,11 @@
 
 static void cubicOp121(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 3,2, 4,3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(3,4, 1,0, 4,3);
     pathB.close();
@@ -3711,11 +3711,11 @@
 // FIXME : haven't debugged this failure yet
 static void cubicOp122(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,5, 4,1, 4,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,4);
     pathB.cubicTo(0,4, 1,0, 5,3);
     pathB.close();
@@ -3724,11 +3724,11 @@
 
 static void cubicOp123(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 2,0, 6,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(0,6, 1,0, 5,1);
     pathB.close();
@@ -3759,11 +3759,11 @@
 
 static void cubicOp124(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 6,0, 3,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(0,3, 1,0, 5,1);
     pathB.close();
@@ -3772,11 +3772,11 @@
 
 static void cubicOp125(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,6, 3,1, 6,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,3);
     pathB.cubicTo(2,6, 1,0, 6,3);
     pathB.close();
@@ -3785,11 +3785,11 @@
 
 static void cubicOp126(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,3, 6,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,2, 1,0, 3,0);
     pathB.close();
@@ -3798,11 +3798,11 @@
 
 static void cubicOp127(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(1,5, 6,0, 3,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(0,3, 1,0, 5,1);
     pathB.close();
@@ -3811,11 +3811,11 @@
 
 static void cubicOp128(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(0,3, 3,2, 5,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,3);
     pathB.cubicTo(2,5, 1,0, 3,0);
     pathB.close();
@@ -3824,11 +3824,11 @@
 
 static void cubicOp129(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(3,4, 2,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(1,2, 6,5, 4,3);
     pathB.close();
@@ -3837,11 +3837,11 @@
 
 static void cubicOp130(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(4,6, 3,0, 2,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     pathB.cubicTo(1,2, 6,5, 6,4);
     pathB.close();
@@ -3869,12 +3869,12 @@
 
 static void cubicOp130a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     SkPoint pts[] = { {5,6}, {4,6}, {3,0}, {2,1} };
     complex_to_quads(pts, &path);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     SkPoint pts2[] = { {0,3}, {1,2}, {6,5}, {6,4} };
     complex_to_quads(pts2, &path);
@@ -3884,11 +3884,11 @@
 
 static void cubicOp131(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(3,4, 3,0, 6,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     pathB.cubicTo(2,6, 1,0, 4,3);
     pathB.close();
@@ -3897,25 +3897,25 @@
 
 static void circlesOp1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addCircle(0, 1, 2, SkPath::kCCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addCircle(0, 1, 1, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addCircle(0, 1, 2, SkPathDirection::kCCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addCircle(0, 1, 1, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
 static void circlesOp2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addCircle(0, 1, 4, SkPath::kCCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addCircle(0, 4, 3, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addCircle(0, 1, 4, SkPathDirection::kCCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addCircle(0, 4, 3, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kIntersect_SkPathOp, filename);
 }
 
 static void rRect1x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(20.65f, 5.65f);
     path.conicTo(20.65f, 1.13612f, 25.1404f, 0.65f, 0.888488f);
     path.lineTo(25.65f, 0.65f);
@@ -3941,7 +3941,7 @@
     SkPath path1(path);
 
     path.reset();
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(20.65f, 45.65f);
     path.lineTo(20.65f, 25.65f);
     path.conicTo(20.65f, 20.65f, 25.65f, 20.65f, 0.707107f);
@@ -3970,12 +3970,12 @@
 
 static void rects5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
-    path.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
-    pathB.addRect(0, 0, 6, 6, SkPath::kCW_Direction);
-    pathB.addRect(5, 5, 6, 6, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(5, 5, 6, 6, SkPathDirection::kCW);
+    path.addRect(5, 5, 6, 6, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
+    pathB.addRect(0, 0, 6, 6, SkPathDirection::kCW);
+    pathB.addRect(5, 5, 6, 6, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
@@ -4003,10 +4003,10 @@
 
 static void circlesOp3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addCircle(0, 1, 2, SkPath::kCCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addCircle(3, 5, 3, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addCircle(0, 1, 2, SkPathDirection::kCCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addCircle(3, 5, 3, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
@@ -4034,11 +4034,11 @@
 
 static void cubicOp132(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(3,4, 3,0, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,3);
     pathB.cubicTo(2,3, 6,5, 4,3);
     pathB.close();
@@ -4058,11 +4058,11 @@
 
 static void cubicOp133(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(5,6, 5,0, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,4, 6,5, 6,5);
     pathB.close();
@@ -4071,11 +4071,11 @@
 
 static void cubicOp134(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(5,6, 6,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,3, 6,5, 6,5);
     pathB.close();
@@ -4084,11 +4084,11 @@
 
 static void cubicOp135(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(5,6, 6,0, 4,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,6);
     pathB.cubicTo(1,4, 6,5, 6,5);
     pathB.close();
@@ -4097,11 +4097,11 @@
 
 static void cubicOp136(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(5,6, 5,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,3, 6,5, 6,5);
     pathB.close();
@@ -4110,11 +4110,11 @@
 
 static void cubicOp136a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.quadTo(5,0, 3,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,5);
     pathB.cubicTo(1,3, 6,5, 6,5);
     pathB.close();
@@ -4123,11 +4123,11 @@
 
 static void cubics137(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 5);
     path.cubicTo(3, 6, 1, 0, 3, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(2, 3, 5, 0, 6, 3);
     pathB.close();
@@ -4136,11 +4136,11 @@
 
 static void cubics138(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 5);
     path.cubicTo(3, 6, 1, 0, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(2, 4, 5, 0, 6, 3);
     pathB.close();
@@ -4150,11 +4150,11 @@
 // three curves intersect successfully nearby -- the angle only gets 2 of the 3 pts
 static void cubicOp139(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(0,4, 3,1, 5,1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,3);
     pathB.cubicTo(1,5, 2,0, 4,0);
     pathB.close();
@@ -4163,11 +4163,11 @@
 
 static void cubicOp140(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(1,2, 5,4, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,5);
     pathB.cubicTo(2,3, 2,0, 2,1);
     pathB.close();
@@ -4176,11 +4176,11 @@
 
 static void cubicOp141(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,2);
     path.cubicTo(1,2, 6,4, 3,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4,6);
     pathB.cubicTo(2,3, 2,0, 2,1);
     pathB.close();
@@ -4249,11 +4249,11 @@
 
 static void loops4i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 3);
     path.cubicTo(0, 2, 0, 2, -1.66666663f, 2.16666675f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(0, 2, -1.66666663f, 2.16666675f, 0, 3);
     pathB.close();
@@ -4262,11 +4262,11 @@
 
 static void loops5i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(0, 2, 0, 2, 0.166666672f, 2.66666675f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(0, 2, 0.166666672f, 2.66666675f, 1, 2);
     pathB.close();
@@ -4275,11 +4275,11 @@
 
 static void cubicOp142(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(5,6);
     path.cubicTo(2,5, 2,1, 1,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1,2);
     pathB.cubicTo(0,1, 6,5, 5,2);
     pathB.close();
@@ -4288,11 +4288,11 @@
 
 static void cubics6d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 5);
     path.cubicTo(1, 5, 4, 2, 4, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(0, 4, 5, 3, 5, 1);
     pathB.close();
@@ -4301,11 +4301,11 @@
 
 static void cubics7d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(2, 4, 5, 1, 3, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(1, 3, 6, 2, 4, 2);
     pathB.close();
@@ -4314,11 +4314,11 @@
 
 static void cubics8d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 5);
     path.cubicTo(2, 4, 5, 1, 3, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(2, 3, 5, 2, 4, 2);
     pathB.close();
@@ -4327,11 +4327,11 @@
 
 static void cubics9d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(2, 6, 3, 1, 5, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(1, 5, 4, 2, 6, 2);
     pathB.close();
@@ -4340,11 +4340,11 @@
 
 static void cubics10u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(1, 6, 4, 1, 5, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(1, 5, 4, 2, 6, 1);
     pathB.close();
@@ -4353,11 +4353,11 @@
 
 static void cubics11i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(2, 5, 3, 2, 5, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 3);
     pathB.cubicTo(1, 5, 4, 2, 5, 2);
     pathB.close();
@@ -4366,11 +4366,11 @@
 
 static void cubics12d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(0, 4, 5, 3, 5, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 5);
     pathB.cubicTo(1, 5, 4, 2, 4, 0);
     pathB.close();
@@ -4379,11 +4379,11 @@
 
 static void cubics13d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(1, 5, 4, 2, 5, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(2, 5, 3, 2, 5, 1);
     pathB.close();
@@ -4392,11 +4392,11 @@
 
 static void cubics14d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(0, 4, 3, 1, 3, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(0, 3, 3, 2, 4, 0);
     pathB.close();
@@ -4405,11 +4405,11 @@
 
 static void cubics15d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(3, 5, 4, 0, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(2, 4, 5, 1, 5, 3);
     pathB.close();
@@ -4418,11 +4418,11 @@
 
 static void cubics16i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(2, 5, 5, 0, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(2, 4, 5, 1, 5, 2);
     pathB.close();
@@ -4431,11 +4431,11 @@
 
 static void cubics17d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(3, 4, 4, 1, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(2, 4, 5, 1, 4, 3);
     pathB.close();
@@ -4444,11 +4444,11 @@
 
 static void cubics18d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(1, 3, 4, 0, 2, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(0, 2, 5, 1, 3, 1);
     pathB.close();
@@ -4457,11 +4457,11 @@
 
 static void cubics19d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(2, 3, 5, 2, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 5);
     pathB.cubicTo(2, 4, 5, 1, 3, 2);
     pathB.close();
@@ -4470,11 +4470,11 @@
 
 static void cubicOp157(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1,5);
     path.cubicTo(1,3, 6,2, 4,2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2,6);
     pathB.cubicTo(2,4, 5,1, 3,1);
     pathB.close();
@@ -4483,11 +4483,11 @@
 
 static void cubics20d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(0, 3, 6, 0, 3, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 6);
     pathB.cubicTo(2, 3, 2, 1, 3, 0);
     pathB.close();
@@ -4496,11 +4496,11 @@
 
 static void loops20i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(0, 2, 0.833333313f, 2, 1, 3.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(0.833333313f, 2, 1, 3.66666651f, 1, 2);
     pathB.close();
@@ -4509,11 +4509,11 @@
 
 static void loops21i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(0, 2, 0.833333313f, 2, 1, 4);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(0.833333313f, 2, 1, 4, 1, 2);
     pathB.close();
@@ -4522,11 +4522,11 @@
 
 static void loops22i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 3);
     path.cubicTo(0, 3, 0.833333313f, 3, 1, 4.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 3);
     pathB.cubicTo(0.833333313f, 3, 1, 4.66666651f, 1, 3);
     pathB.close();
@@ -4535,11 +4535,11 @@
 
 static void loops23i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(0, 1, 6.16666698f, 5.66666698f, -5.66666651f, 6.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(6.16666698f, 5.66666698f, -5.66666651f, 6.66666651f, 1, 5);
     pathB.close();
@@ -4548,11 +4548,11 @@
 
 static void loops24i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(0, 2, 0.833333313f, 2, 1, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(0.833333313f, 2, 1, 3, 1, 2);
     pathB.close();
@@ -4561,11 +4561,11 @@
 
 static void loops25i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(0, 5, 0.833333313f, 5, 1, 7);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(0.833333313f, 5, 1, 7, 1, 5);
     pathB.close();
@@ -4574,11 +4574,11 @@
 
 static void loops26i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 6);
     path.cubicTo(0, 2, 6.16666698f, 6.66666698f, -5.66666651f, 7.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(6.16666698f, 6.66666698f, -5.66666651f, 7.66666651f, 1, 6);
     pathB.close();
@@ -4587,11 +4587,11 @@
 
 static void loops27i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 3);
     path.cubicTo(0, 3, 0.833333313f, 3, 1, 4.33333349f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 3);
     pathB.cubicTo(0.833333313f, 3, 1, 4.33333349f, 1, 3);
     pathB.close();
@@ -4600,11 +4600,11 @@
 
 static void loops28i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(1, 3, 1.83333337f, 3, 2, 4.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(1.83333337f, 3, 2, 4.66666651f, 2, 3);
     pathB.close();
@@ -4613,11 +4613,11 @@
 
 static void loops29i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(0, 4, 1.66666663f, 4, 2, 7.33333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(1.66666663f, 4, 2, 7.33333302f, 2, 4);
     pathB.close();
@@ -4626,11 +4626,11 @@
 
 static void loops30i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(0, 4, 1.66666663f, 4, 2, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(1.66666663f, 4, 2, 8, 2, 4);
     pathB.close();
@@ -4639,11 +4639,11 @@
 
 static void loops31i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 5);
     path.cubicTo(1, 5, 1.83333337f, 5, 2, 6.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(1.83333337f, 5, 2, 6.66666651f, 2, 5);
     pathB.close();
@@ -4652,11 +4652,11 @@
 
 static void loops32i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(1, 6, 1.83333337f, 6, 2, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 6);
     pathB.cubicTo(1.83333337f, 6, 2, 8, 2, 6);
     pathB.close();
@@ -4665,11 +4665,11 @@
 
 static void loops33i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6);
     pathB.close();
@@ -4691,11 +4691,11 @@
  //       }
         pts[5].fY = 6.66666698f + offset;
         SkPath path, pathB;
-        path.setFillType(SkPath::kWinding_FillType);
+        path.setFillType(SkPathFillType::kWinding);
         path.moveTo(pts[0]);
         path.cubicTo(pts[1], pts[2], pts[3]);
         path.close();
-        pathB.setFillType(SkPath::kWinding_FillType);
+        pathB.setFillType(SkPathFillType::kWinding);
         pathB.moveTo(pts[4]);
         pathB.cubicTo(pts[5], pts[6], pts[7]);
         pathB.close();
@@ -4712,11 +4712,11 @@
 
 static void loops33iAsQuads(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(1, 2, 7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(7.16666698f, 6.66666698f, -4.66666651f, 7.66666651f, 2, 6);
     pathB.close();
@@ -4728,11 +4728,11 @@
 
 static void loops34i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(0, 4, 2.5f, 4, 3, 9);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(2.5f, 4, 3, 9, 3, 4);
     pathB.close();
@@ -4741,11 +4741,11 @@
 
 static void loops35i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(0, 4, 2.5f, 4, 3, 10);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(2.5f, 4, 3, 10, 3, 4);
     pathB.close();
@@ -4754,11 +4754,11 @@
 
 static void loops36i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(1, 4, 2.66666675f, 4, 3, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(2.66666675f, 4, 3, 8, 3, 4);
     pathB.close();
@@ -4767,11 +4767,11 @@
 
 static void loops37i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(1, 4, 1.83333337f, 4, 2, 5.33333349f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(1.83333337f, 4, 2, 5.33333349f, 2, 4);
     pathB.close();
@@ -4780,11 +4780,11 @@
 
 static void loops38i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(2, 4, 2.83333325f, 4, 3, 6);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(2.83333325f, 4, 3, 6, 3, 4);
     pathB.close();
@@ -4793,11 +4793,11 @@
 
 static void loops39i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 5);
     path.cubicTo(0, 5, 2.5f, 5, 3, 10);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(2.5f, 5, 3, 10, 3, 5);
     pathB.close();
@@ -4806,11 +4806,11 @@
 
 static void loops40i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 5);
     path.cubicTo(0, 5, 2.5f, 5, 3, 11);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(2.5f, 5, 3, 11, 3, 5);
     pathB.close();
@@ -4819,11 +4819,11 @@
 
 static void loops40iAsQuads(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 5);
     path.cubicTo(0, 5, 2.5f, 5, 3, 11);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(2.5f, 5, 3, 11, 3, 5);
     pathB.close();
@@ -4835,11 +4835,11 @@
 
 static void loops44i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 5);
     path.cubicTo(0, 1, 7.33333302f, 5.33333349f, -7, 7);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(7.33333302f, 5.33333349f, -7, 7, 1, 5);
     pathB.close();
@@ -4848,11 +4848,11 @@
 
 static void loops45i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 6);
     path.cubicTo(0, 2, 7.33333302f, 6.33333302f, -7, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(7.33333302f, 6.33333302f, -7, 8, 1, 6);
     pathB.close();
@@ -4861,11 +4861,11 @@
 
 static void loops46i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(1, 2, 8.33333302f, 6.33333302f, -6, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(8.33333302f, 6.33333302f, -6, 8, 2, 6);
     pathB.close();
@@ -4876,11 +4876,11 @@
 FAILED: d:\cygwin\puregit\tests\pathopsextendedtest.cpp:346    0 */
 static void loops47i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(0, 1, 6, 5.83333302f, -4, 8);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(6, 5.83333302f, -4, 8, 2, 4);
     pathB.close();
@@ -4889,11 +4889,11 @@
 
 static void loops48i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 6);
     path.cubicTo(0, 1, 9.33333302f, 6.83333302f, -8.33333302f, 9.16666603f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(9.33333302f, 6.83333302f, -8.33333302f, 9.16666603f, 2, 6);
     pathB.close();
@@ -4902,11 +4902,11 @@
 
 static void loops49i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 2);
     path.cubicTo(1, 4, -0.166666687f, 2.66666675f, 1.66666675f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(-0.166666687f, 2.66666675f, 1.66666675f, 2, 0, 2);
     pathB.close();
@@ -4915,11 +4915,11 @@
 
 static void loops50i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 3);
     path.cubicTo(1, 5, -0.166666687f, 3.66666675f, 1.66666675f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(-0.166666687f, 3.66666675f, 1.66666675f, 3, 0, 3);
     pathB.close();
@@ -4928,11 +4928,11 @@
 
 static void loops51i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(2, 4, 0.833333313f, 2.66666675f, 2.66666675f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(0.833333313f, 2.66666675f, 2.66666675f, 2, 1, 2);
     pathB.close();
@@ -4941,11 +4941,11 @@
 
 static void loops52i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 3);
     path.cubicTo(2, 5, 0.833333313f, 3.66666675f, 2.66666675f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 5);
     pathB.cubicTo(0.833333313f, 3.66666675f, 2.66666675f, 3, 1, 3);
     pathB.close();
@@ -4954,11 +4954,11 @@
 
 static void loops53i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(3, 5, 1.83333325f, 3.66666675f, 3.66666651f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 5);
     pathB.cubicTo(1.83333325f, 3.66666675f, 3.66666651f, 3, 2, 3);
     pathB.close();
@@ -4967,11 +4967,11 @@
 
 static void loops54i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 2);
     path.cubicTo(1, 4, 0, 3, 1.66666675f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 4);
     pathB.cubicTo(0, 3, 1.66666675f, 2, 0, 2);
     pathB.close();
@@ -4980,11 +4980,11 @@
 
 static void loops55i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 3);
     path.cubicTo(1, 5, 0, 4, 1.66666675f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(0, 4, 1.66666675f, 3, 0, 3);
     pathB.close();
@@ -4993,11 +4993,11 @@
 
 static void loops56i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 2);
     path.cubicTo(2, 4, 0.99999994f, 3, 2.66666675f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(0.99999994f, 3, 2.66666675f, 2, 1, 2);
     pathB.close();
@@ -5006,11 +5006,11 @@
 
 static void loops57i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 3);
     path.cubicTo(2, 5, 0.99999994f, 4, 2.66666675f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 5);
     pathB.cubicTo(0.99999994f, 4, 2.66666675f, 3, 1, 3);
     pathB.close();
@@ -5019,11 +5019,11 @@
 
 static void loops58i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(3, 5, 2, 4, 3.66666651f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 5);
     pathB.cubicTo(2, 4, 3.66666651f, 3, 2, 3);
     pathB.close();
@@ -5032,11 +5032,11 @@
 
 static void loops58iAsQuads(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(3, 5, 2, 4, 3.66666651f, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 5);
     pathB.cubicTo(2, 4, 3.66666651f, 3, 2, 3);
     pathB.close();
@@ -5051,11 +5051,11 @@
 
 static void loops59i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 6);
     path.cubicTo(1, 2, 7.33333302f, 1.66666663f, -7.5f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(7.33333302f, 1.66666663f, -7.5f, 2, 0, 6);
     pathB.close();
@@ -5064,11 +5064,11 @@
 
 static void loops59iasQuads(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 6);
     path.cubicTo(1, 2, 7.33333302f, 1.66666663f, -7.5f, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 2);
     pathB.cubicTo(7.33333302f, 1.66666663f, -7.5f, 2, 0, 6);
     pathB.close();
@@ -5083,11 +5083,11 @@
 
 static void cubics41d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(1, 4, 3, 0, 3, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 3);
     pathB.cubicTo(1, 3, 1, 0, 4, 1);
     pathB.close();
@@ -5096,11 +5096,11 @@
 
 void loops61i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(1, 5, -6.33333302f, 0.666666627f, 8, -1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(-6.33333302f, 0.666666627f, 8, -1, 0, 1);
     pathB.close();
@@ -5109,11 +5109,11 @@
 
 static void loops62i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 2);
     path.cubicTo(1, 6, -6.33333302f, 1.66666663f, 8, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 6);
     pathB.cubicTo(-6.33333302f, 1.66666663f, 8, 0, 0, 2);
     pathB.close();
@@ -5122,11 +5122,11 @@
 
 static void loops63i(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(2, 4, -4, -0.833333254f, 6, -3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 4);
     pathB.cubicTo(-4, -0.833333254f, 6, -3, 0, 1);
     pathB.close();
@@ -5135,11 +5135,11 @@
 
 static void cubics44d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(2, 5, 3, 1, 6, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 3);
     pathB.cubicTo(2, 6, 4, 3, 5, 2);
     pathB.close();
@@ -5148,11 +5148,11 @@
 
 static void cubics45u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 3);
     path.cubicTo(2, 6, 4, 3, 5, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(3, 4);
     pathB.cubicTo(2, 5, 3, 1, 6, 2);
     pathB.close();
@@ -5175,7 +5175,7 @@
 // to produce meaningful results
 static void crbug_526025(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x43b40000), SkBits2Float(0xcf000000));  // 360, -2.14748e+09f
 path.cubicTo(SkBits2Float(0x4e0d628f), SkBits2Float(0xceffffff), SkBits2Float(0x4e800003), SkBits2Float(0xcec6b143), SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffc));  // 5.93012e+08f, -2.14748e+09f, 1.07374e+09f, -1.66675e+09f, 1.07374e+09f, -1.07374e+09f
 path.cubicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcde53aee), SkBits2Float(0x4e0d6292), SkBits2Float(0xc307820e), SkBits2Float(0x44627d00), SkBits2Float(0x437ffff2));  // 1.07374e+09f, -4.80731e+08f, 5.93012e+08f, -135.508f, 905.953f, 256
@@ -5189,7 +5189,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42fe0000), SkBits2Float(0x43a08000));  // 127, 321
 path.lineTo(SkBits2Float(0x45d5c000), SkBits2Float(0x43870000));  // 6840, 270
 path.lineTo(SkBits2Float(0xd0a00000), SkBits2Float(0x4cbebc20));  // -2.14748e+10f, 1e+08
@@ -5203,7 +5203,7 @@
 
 static void fuzzX_392(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bde212));  // 29, 379.766f
 path.lineTo(SkBits2Float(0x41e80000), SkBits2Float(0x43bdc7ef));  // 29, 379.562f
 path.conicTo(SkBits2Float(0x42a5861e), SkBits2Float(0x43c61f86), SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86), SkBits2Float(0x3f7d23f3));  // 82.7619f, 396.246f, 139.024f, 396.246f, 0.98883f
@@ -5211,7 +5211,7 @@
 path.close();
 
     SkPath path1(path);
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0xc36c7bd8), SkBits2Float(0xc3a31d72));  // -236.484f, -326.23f
 path.lineTo(SkBits2Float(0xc367a4ae), SkBits2Float(0xc3a31d72));  // -231.643f, -326.23f
 path.lineTo(SkBits2Float(0x430b0610), SkBits2Float(0x43c61f86));  // 139.024f, 396.246f
@@ -5223,7 +5223,7 @@
 
 static void dean2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
 path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
 path.cubicTo(SkBits2Float(0x414a835a), SkBits2Float(0x3ec07ba6), SkBits2Float(0x413fcc0d), SkBits2Float(0x3e193319), SkBits2Float(0x4134a02b), SkBits2Float(0x00000000)); // 12.6571f, 0.375943f, 11.9873f, 0.149609f, 11.2891f, 0
@@ -5232,7 +5232,7 @@
     SkPath path1(path);
 
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b)); // 0.669749f, 13.2891f
 path.cubicTo(SkBits2Float(0x3f2b74b3), SkBits2Float(0x4154a02b), SkBits2Float(0x41531912), SkBits2Float(0x3f130322), SkBits2Float(0x4154a02b), SkBits2Float(0x3f2b74b3)); // 0.669749f, 13.2891f, 13.1936f, 0.574267f, 13.2891f, 0.669749f
 path.lineTo(SkBits2Float(0x417ab74b), SkBits2Float(0x4154a02b)); // 15.6697f, 13.2891f
@@ -5244,11 +5244,11 @@
 
 static void cubics_d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(3, 5, 1, 0, 3, 0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 1);
     pathB.cubicTo(0, 3, 1, 0, 5, 3);
     pathB.close();
@@ -5257,11 +5257,11 @@
 
 static void cubics_d2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 1);
     path.cubicTo(2, 5, 2, 0, 2, 1);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 2);
     pathB.cubicTo(1, 2, 1, 0, 5, 2);
     pathB.close();
@@ -5270,11 +5270,11 @@
 
 static void loops_i1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 3);
     path.cubicTo(0, 4, -0.333333343f, 4.66666651f, 3, 5.83333349f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 4);
     pathB.cubicTo(-0.333333343f, 4.66666651f, 3, 5.83333349f, 2, 3);
     pathB.close();
@@ -5283,11 +5283,11 @@
 
 static void loops_i2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 4);
     path.cubicTo(0, 5, -0.333333343f, 5.66666651f, 3, 6.83333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(-0.333333343f, 5.66666651f, 3, 6.83333302f, 2, 4);
     pathB.close();
@@ -5296,11 +5296,11 @@
 
 static void loops_i3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(2, 5);
     path.cubicTo(0, 6, -0.333333343f, 6.66666651f, 3, 7.83333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 6);
     pathB.cubicTo(-0.333333343f, 6.66666651f, 3, 7.83333302f, 2, 5);
     pathB.close();
@@ -5309,11 +5309,11 @@
 
 static void loops_i4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(1, 5, 0.666666627f, 5.66666651f, 4, 6.83333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 5);
     pathB.cubicTo(0.666666627f, 5.66666651f, 4, 6.83333302f, 3, 4);
     pathB.close();
@@ -5322,11 +5322,11 @@
 
 static void loops_i5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 5);
     path.cubicTo(1, 6, 0.666666627f, 6.66666651f, 4, 7.83333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 6);
     pathB.cubicTo(0.666666627f, 6.66666651f, 4, 7.83333302f, 3, 5);
     pathB.close();
@@ -5335,11 +5335,11 @@
 
 static void loops_i6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(4, 5);
     path.cubicTo(2, 6, 1.66666663f, 6.66666651f, 5, 7.83333302f);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 6);
     pathB.cubicTo(1.66666663f, 6.66666651f, 5, 7.83333302f, 4, 5);
     pathB.close();
@@ -5348,11 +5348,11 @@
 
 static void cubics_d3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(3, 4);
     path.cubicTo(0, 6, 6, 1, 4, 2);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1, 6);
     pathB.cubicTo(2, 4, 4, 3, 6, 0);
     pathB.close();
@@ -5361,11 +5361,11 @@
 
 static void cubics_o(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(1, 4);
     path.cubicTo(2, 6, 5, 0, 5, 3);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 5);
     pathB.cubicTo(3, 5, 4, 1, 6, 2);
     pathB.close();
@@ -5374,11 +5374,11 @@
 
 static void cubicOp158(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0,1);
     path.cubicTo(2,4, 2,0, 2,0);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0,2);
     pathB.cubicTo(0,2, 1,0, 4,2);
     pathB.close();
@@ -5398,10 +5398,10 @@
 
 static void circlesOp4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addCircle(0, 1, 5, SkPath::kCW_Direction);
-    pathB.setFillType(SkPath::kWinding_FillType);
-    pathB.addCircle(0, 1, 0, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addCircle(0, 1, 5, SkPathDirection::kCW);
+    pathB.setFillType(SkPathFillType::kWinding);
+    pathB.addCircle(0, 1, 0, SkPathDirection::kCW);
     testPathOp(reporter, path, pathB, kDifference_SkPathOp, filename);
 }
 
@@ -5435,7 +5435,7 @@
 
 static void seanbug(skiatest::Reporter* reporter, const char* filename) {
    SkPath path;
-   path.setFillType(SkPath::kEvenOdd_FillType);
+   path.setFillType(SkPathFillType::kEvenOdd);
    path.moveTo(SkBits2Float(0x45b56000), SkBits2Float(0x45bca000));  // 5804, 6036
    path.lineTo(SkBits2Float(0x45b55f0a), SkBits2Float(0x45bc9fc0));  // 5803.88f, 6035.97f
    path.lineTo(SkBits2Float(0x45b55e15), SkBits2Float(0x45bc9f7b));  // 5803.76f, 6035.94f
@@ -5715,7 +5715,7 @@
    path.close();
 
    SkPath path2;
-   path2.setFillType(SkPath::kWinding_FillType);
+   path2.setFillType(SkPathFillType::kWinding);
    path2.moveTo(SkBits2Float(0x45b52600), SkBits2Float(0x45ba7c62));  // 5796.75f, 5967.55f
    path2.lineTo(SkBits2Float(0x45c7dc6b), SkBits2Float(0x45ba7c62));  // 6395.55f, 5967.55f
    path2.lineTo(SkBits2Float(0x45c7dc6b), SkBits2Float(0x45bca239));  // 6395.55f, 6036.28f
@@ -5729,16 +5729,16 @@
 
 static void halbug(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, path2;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.addRect(SkRect{278.653992f, 155.747406f, 580.15918f, 593.602051f});
-    path2.setFillType(SkPath::kWinding_FillType);
+    path2.setFillType(SkPathFillType::kWinding);
     path2.addRect(SkRect{278.657715f, 155.747314f, 580.238281f, 594.114014f});
     testPathOp(reporter, path, path2, kIntersect_SkPathOp, filename);
 }
 
 static void testRect1_u(skiatest::Reporter* reporter, const char* filename) {
     SkPath path, pathB;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 0);
     path.lineTo(0, 60);
     path.lineTo(60, 60);
@@ -5754,13 +5754,13 @@
     path.lineTo(36, 30);
     path.lineTo(36, 20);
     path.close();
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     testPathOp(reporter, path, pathB, kUnion_SkPathOp, filename);
 }
 
 static void filinmangust14(skiatest::Reporter* reporter, const char* filename) {
 SkPath path, path1;
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
         path.moveTo(SkBits2Float(0x440bc02c), SkBits2Float(0x4409c000));  // 559.003f, 551
         path.lineTo(SkBits2Float(0x440bc02c), SkBits2Float(0x440e8000));  // 559.003f, 570
         path.lineTo(SkBits2Float(0x440bbfda), SkBits2Float(0x440e8000));  // 558.998f, 570
@@ -5769,7 +5769,7 @@
         path.close();
 path1 = path;
 path.reset();
-        path.setFillType(SkPath::kWinding_FillType);
+        path.setFillType(SkPathFillType::kWinding);
         path.moveTo(SkBits2Float(0x45582000), SkBits2Float(0x45be9805));  // 3458, 6099
         path.lineTo(SkBits2Float(0x4554b667), SkBits2Float(0x45be9805));  // 3403.4f, 6099
         path.lineTo(SkBits2Float(0x4554b667), SkBits2Float(0x45be97fb));  // 3403.4f, 6099
@@ -5805,7 +5805,7 @@
 
 static void grshapearcs1(skiatest::Reporter* reporter, const char* filename) {
 SkPath path, path1;
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(25.0098f, 23.1973f);
 path.lineTo(25.5689f, 22.3682f);
 path.conicTo(26.1281f, 21.5392f, 26.9572f, 22.0984f, 0.707107f);
@@ -7377,7 +7377,7 @@
 path.close();
 path1 = path;
 path.reset();
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(25.0098f, 23.1973f);
 path.lineTo(25.5689f, 22.3682f);
 path.conicTo(26.1281f, 21.5392f, 26.9572f, 22.0984f, 0.707107f);
@@ -8952,15 +8952,15 @@
 
 static void op_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x15e80300), SkBits2Float(0x400004dc));  // 9.37088e-26f, 2.0003f
 path.quadTo(SkBits2Float(0xe56c206c), SkBits2Float(0x646c5f40), SkBits2Float(0x6c80885e), SkBits2Float(0xb4bc576c));  // -6.96923e+22f, 1.74412e+22f, 1.24309e+27f, -3.50813e-07f
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x1b000010), SkBits2Float(0x6e5a5a1b));  // 1.05879e-22f, 1.68942e+28f
 path.quadTo(SkBits2Float(0xef646464), SkBits2Float(0xefefefef), SkBits2Float(0x000000ef), SkBits2Float(0x1bb4bc00));  // -7.06839e+28f, -1.48514e+29f, 3.3491e-43f, 2.99e-22f
 
@@ -8971,8 +8971,8 @@
 
 static void op_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
-path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType((SkPathFillType) 1);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xeee3ef57), SkBits2Float(0xef6300f8));  // -3.52712e+28f, -7.02543e+28f
 path.quadTo(SkBits2Float(0xeeee9c6e), SkBits2Float(0xef609993), SkBits2Float(0x00000000), SkBits2Float(0x6e5a5a1b));  // -3.69233e+28f, -6.95103e+28f, 0, 1.68942e+28f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -8983,8 +8983,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x1b1b1b00), SkBits2Float(0x1b5a5a1b));  // 1.283e-22f, 1.80617e-22f
 
@@ -8995,8 +8995,8 @@
 
 static void op_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
-path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType((SkPathFillType) 1);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6e5a5a1b));  // 0, 1.68942e+28f
 path.quadTo(SkBits2Float(0xeeee9c6e), SkBits2Float(0xef609993), SkBits2Float(0xeee3ef57), SkBits2Float(0xef6300f8));  // -3.69233e+28f, -6.95103e+28f, -3.52712e+28f, -7.02543e+28f
 path.quadTo(SkBits2Float(0xeeda2c5a), SkBits2Float(0xef6533a7), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // -3.37607e+28f, -7.09345e+28f, 0, 0
@@ -9010,8 +9010,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
@@ -9020,7 +9020,7 @@
 static void op_4(skiatest::Reporter* reporter, const char* filename) {
    SkPath patha, pathb;
 
-   patha.setFillType(SkPath::kEvenOdd_FillType);
+   patha.setFillType(SkPathFillType::kEvenOdd);
    patha.moveTo(SkBits2Float(0x40d7ea90), SkBits2Float(0x3fa58930));  // 6.74738f, 1.29325f
    patha.lineTo(SkBits2Float(0x40ad3d93), SkBits2Float(0x3fa58930));  // 5.41377f, 1.29325f
    patha.lineTo(SkBits2Float(0x40ad3d93), SkBits2Float(0x3edba819));  // 5.41377f, 0.429017f
@@ -9030,7 +9030,7 @@
    patha.lineTo(SkBits2Float(0x40d7ea90), SkBits2Float(0x3fa58930));  // 6.74738f, 1.29325f
    patha.close();
 
-   pathb.setFillType(SkPath::kEvenOdd_FillType);
+   pathb.setFillType(SkPathFillType::kEvenOdd);
    pathb.moveTo(SkBits2Float(0x40d7ea89), SkBits2Float(0x409a721d));  // 6.74738f, 4.82643f
    pathb.lineTo(SkBits2Float(0x411a9d73), SkBits2Float(0x409a721d));  // 9.66344f, 4.82643f
    pathb.lineTo(SkBits2Float(0x411a9d73), SkBits2Float(0x3f3b7c9a));  // 9.66344f, 0.73237f
@@ -9081,7 +9081,7 @@
 
 static void bug8380(skiatest::Reporter* reporter, const char* filename) {
 SkPath path, path2;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xa6800000), SkBits2Float(0x43b0f22d));  // -8.88178e-16f, 353.892f
 path.lineTo(SkBits2Float(0x42fc0000), SkBits2Float(0x4116566d));  // 126, 9.3961f
 path.cubicTo(SkBits2Float(0x42fb439d), SkBits2Float(0x4114bbc7), SkBits2Float(0x42fa3ed7), SkBits2Float(0x411565bd), SkBits2Float(0x42f934d2), SkBits2Float(0x4116131e));  // 125.632f, 9.29584f, 125.123f, 9.33734f, 124.603f, 9.37967f
@@ -9092,7 +9092,7 @@
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x43b0f22d));  // 0, 353.892f
 path.lineTo(SkBits2Float(0xa6800000), SkBits2Float(0x43b0f22d));  // -8.88178e-16f, 353.892f
 path.close();
-path2.setFillType(SkPath::kEvenOdd_FillType);
+path2.setFillType(SkPathFillType::kEvenOdd);
 path2.moveTo(SkBits2Float(0x4102c0ec), SkBits2Float(0x42d06d0e));  // 8.1721f, 104.213f
 path2.lineTo(SkBits2Float(0xc0ba5a1d), SkBits2Float(0x43b8e831));  // -5.8235f, 369.814f
 path2.lineTo(SkBits2Float(0x42fc0000), SkBits2Float(0x411656d6));  // 126, 9.3962f
@@ -9612,9 +9612,9 @@
 
 static void fuzz535151(skiatest::Reporter* reporter, const char* filename) {
     SkPath one;
-    one.setFillType(SkPath::kWinding_FillType);
+    one.setFillType(SkPathFillType::kWinding);
     SkPath two;
-    two.setFillType(SkPath::kWinding_FillType);
+    two.setFillType(SkPathFillType::kWinding);
     two.moveTo(0, 0);
     two.lineTo(0, 50);
     two.lineTo(4.29497e+09f, 50);
@@ -9652,7 +9652,7 @@
 
 static void fuzz433b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path1, path2;
-    path1.setFillType(SkPath::kEvenOdd_FillType);
+    path1.setFillType(SkPathFillType::kEvenOdd);
     path1.moveTo(140, 40);
     path1.lineTo(200, 210);
     path1.lineTo(40, 100);
@@ -9661,7 +9661,7 @@
     path1.lineTo(140, 40);
     path1.close();
 
-    path1.setFillType(SkPath::kWinding_FillType);
+    path1.setFillType(SkPathFillType::kWinding);
     path2.moveTo(190, 60);
     path2.lineTo(250, 230);
     path2.lineTo(90, 120);
@@ -9675,7 +9675,7 @@
 
 static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
 path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x429a6666), SkBits2Float(0x42f9999a), SkBits2Float(0x4275999a), SkBits2Float(0x42d70001), SkBits2Float(0x42633333));
@@ -9694,7 +9694,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
 path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x42a20000), SkBits2Float(0x43016667), SkBits2Float(0x4287cccd), SkBits2Float(0x42ea999a), SkBits2Float(0x4273999a));
@@ -9721,7 +9721,7 @@
 
 static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
 path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x429a6666), SkBits2Float(0x42f9999a), SkBits2Float(0x4275999a), SkBits2Float(0x42d70001), SkBits2Float(0x42633333));
@@ -9740,7 +9740,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
 path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
 path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x42a20000), SkBits2Float(0x43016667), SkBits2Float(0x4287cccd), SkBits2Float(0x42ea999a), SkBits2Float(0x4273999a));
@@ -9767,7 +9767,7 @@
 
 static void fuzz714(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x430c0000), SkBits2Float(0x42200000));
 path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43520000));
 path.lineTo(SkBits2Float(0x42200000), SkBits2Float(0x42c80000));
@@ -9778,7 +9778,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43200000), SkBits2Float(0x42700000));
 path.lineTo(SkBits2Float(0x435c0000), SkBits2Float(0x43660000));
 path.lineTo(SkBits2Float(0x42700000), SkBits2Float(0x42f00000));
@@ -9793,7 +9793,7 @@
 
 static void fuzz1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
 path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
 path.quadTo(SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000), SkBits2Float(0x7f800000));
@@ -9807,7 +9807,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 2, filename);
@@ -9816,7 +9816,7 @@
 
 static void fuzz753_91(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42910000), SkBits2Float(0x00000000));  // 72.5f, 0
 path.lineTo(SkBits2Float(0x42166668), SkBits2Float(0x00000000));  // 37.6f, 0
 path.cubicTo(SkBits2Float(0x42166668), SkBits2Float(0xc1966668), SkBits2Float(0x41c66668), SkBits2Float(0xc20a6666), SkBits2Float(0x40f00010), SkBits2Float(0xc21ccccd));  // 37.6f, -18.8f, 24.8f, -34.6f, 7.50001f, -39.2f
@@ -9826,7 +9826,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x428bf702), SkBits2Float(0xcf223cbf));  // 69.9824f, -2.72189e+09f
 path.lineTo(SkBits2Float(0x42112d68), SkBits2Float(0xcf223cbf));  // 36.2943f, -2.72189e+09f
 path.cubicTo(SkBits2Float(0x4220d9fc), SkBits2Float(0xcf223cc0), SkBits2Float(0x420ee118), SkBits2Float(0xcf223cc0), SkBits2Float(0x41cef2f8), SkBits2Float(0xcf223cc0));  // 40.2129f, -2.72189e+09f, 35.7198f, -2.72189e+09f, 25.8686f, -2.72189e+09f
@@ -9840,7 +9840,7 @@
 
 static void bug597926_0(skiatest::Reporter* reporter, const char* filename) {
 SkPath path;
-path.setFillType((SkPath::FillType) 0);
+path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43b38000), SkBits2Float(0x433e0000));  // 359, 190
 path.lineTo(SkBits2Float(0x40c00000), SkBits2Float(0x449ce000));  // 6, 1255
 path.cubicTo(SkBits2Float(0x438c0000), SkBits2Float(0x4497a000), SkBits2Float(0x43e40000), SkBits2Float(0x44750000), SkBits2Float(0x41000000), SkBits2Float(0x44aa2000));  // 280, 1213, 456, 980, 8, 1361
@@ -9851,7 +9851,7 @@
 path.quadTo(SkBits2Float(0x43948000), SkBits2Float(0x42ac0000), SkBits2Float(0x43880000), SkBits2Float(0x4487e000));  // 297, 86, 272, 1087
 SkPath path1(path);
 path.reset();
-path.setFillType((SkPath::FillType) 0);
+path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xc51d735c), SkBits2Float(0xc49db029));  // -2519.21f, -1261.51f
 path.cubicTo(SkBits2Float(0xc51d1dbd), SkBits2Float(0xc49d7a3f), SkBits2Float(0xc51c524a), SkBits2Float(0xc49d1610), SkBits2Float(0xc51d1a96), SkBits2Float(0xc49d86a6));  // -2513.86f, -1259.82f, -2501.14f, -1256.69f, -2513.66f, -1260.21f
 path.cubicTo(SkBits2Float(0xc51cd471), SkBits2Float(0xc49d54d0), SkBits2Float(0xc51c2e51), SkBits2Float(0xc49d0081), SkBits2Float(0xc51d197b), SkBits2Float(0xc49d7927));  // -2509.28f, -1258.65f, -2498.89f, -1256.02f, -2513.59f, -1259.79f
@@ -9882,7 +9882,7 @@
 
 static void fuzz1450_1(skiatest::Reporter* reporter, const char* filename) {
 SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0x4e800002), SkBits2Float(0xce7ffffe));  // 1.07374e+09f, -1.07374e+09f
 path.conicTo(SkBits2Float(0x4e800002), SkBits2Float(0xcf000000), SkBits2Float(0x43b40000), SkBits2Float(0xcf000000), SkBits2Float(0x3f3504f4));  // 1.07374e+09f, -2.14748e+09f, 360, -2.14748e+09f, 0.707107f
 path.lineTo(SkBits2Float(0x43348000), SkBits2Float(0x43800001));  // 180.5f, 256
@@ -9911,11 +9911,11 @@
 
 static void fuzz763_9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a21), SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b), SkBits2Float(0x1f212a8c));  // 2.49282e-13f, 4.78968e-34f, 1.99397e+36f, -4.84373e+27f, 3.41283e-20f
 path.lineTo(SkBits2Float(0x7bc00321), SkBits2Float(0xed7a6a4b));  // 1.99397e+36f, -4.84373e+27f
@@ -9939,11 +9939,11 @@
 
 static void fuzz763_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x555b3a2d), SkBits2Float(0x2a212a8c));  // 1.50652e+13f, 1.43144e-13f
 path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
@@ -9972,11 +9972,11 @@
 
 static void fuzz763_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
 path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x295b2d1f), SkBits2Float(0x29685568));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.86669e-14f, 5.15884e-14f
@@ -10007,11 +10007,11 @@
 
 static void fuzz763_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x2a2a8c55));  // 4.76191e+16f, 1.51477e-13f
 path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b79), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
 path.conicTo(SkBits2Float(0x3a2147ed), SkBits2Float(0xdf28282a), SkBits2Float(0x3a8a3a21), SkBits2Float(0x8a284f9a), SkBits2Float(0x3ac23ab3));  // 0.000615238f, -1.2117e+19f, 0.00105459f, -8.10388e-33f, 0.00148185f
@@ -10032,11 +10032,11 @@
 
 static void fuzz763_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
 path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
@@ -10072,11 +10072,11 @@
 // crbug.com/626164
 static void fuzz763_1c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
     path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
@@ -10091,7 +10091,7 @@
 // crbug.com/626186
 static void fuzz763_1b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.cubicTo(SkBits2Float(0x0000ff07), SkBits2Float(0xf9f9ff00), SkBits2Float(0xfe0ef9f4), SkBits2Float(0xd9b105fb), SkBits2Float(0x000000f9), SkBits2Float(0xfe11f901));  // 9.14866e-41f, -1.62257e+35f, -4.75121e+37f, -6.22846e+15f, 3.48923e-43f, -4.85077e+37f
     path.lineTo(SkBits2Float(0xda1905ed), SkBits2Float(0x3c05fbfb));  // -1.0768e+16f, 0.00817775f
@@ -10099,7 +10099,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.quadTo(SkBits2Float(0x3c3c3c3c), SkBits2Float(0xfa253c3c), SkBits2Float(0xfefa00d3), SkBits2Float(0x25fad9df));  // 0.011489f, -2.14488e+35f, -1.66156e+38f, 4.35157e-16f
     path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -10114,13 +10114,13 @@
 
 static void fuzz763_1a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.cubicTo(SkBits2Float(0x154be880), SkBits2Float(0x80000640), SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0x80045959), SkBits2Float(0x40154be8));  // 4.11789e-26f, -2.24208e-42f, 1.49562e+13f, 7.50652e+15f, -3.99394e-40f, 2.33276f
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
     path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.quadTo(SkBits2Float(0x5559a419), SkBits2Float(0x59d55928), SkBits2Float(0xbd595959), SkBits2Float(0x3f3f3f09));  // 1.49562e+13f, 7.50652e+15f, -0.0530637f, 0.747056f
     path.moveTo(SkBits2Float(0x3f3f3f3f), SkBits2Float(0x3f3f3f3f));  // 0.747059f, 0.747059f
@@ -10142,11 +10142,11 @@
 // crbug.com/627780
 static void fuzz763_3a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x555b292d), SkBits2Float(0x2a212a8c));  // 1.50606e+13f, 1.43144e-13f
 path.conicTo(SkBits2Float(0xc0032108), SkBits2Float(0x7a6a4b7b), SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a3a7bc0));  // -2.04889f, 3.04132e+35f, 5.77848e-19f, 4.7323e-37f, 5.63611e+25f
@@ -10163,7 +10163,7 @@
 // crbug.com/627689
 static void fuzz763_5a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x38bd8610), SkBits2Float(0x00000000));  // 9.03719e-05f, 0
 path.conicTo(SkBits2Float(0x4183d871), SkBits2Float(0x41fea321), SkBits2Float(0xb700ff00), SkBits2Float(0x4240b8b8), SkBits2Float(0x3b058283));  // 16.4807f, 31.8297f, -7.68877e-06f, 48.1804f, 0.0020372f
 path.lineTo(SkBits2Float(0x3a3a3ab8), SkBits2Float(0xb8b8b8b8));  // 0.000710409f, -8.80821e-05f
@@ -10178,7 +10178,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
@@ -10187,11 +10187,11 @@
 // crbug.com/627401
 static void fuzz763_2a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x3e484500), SkBits2Float(0x164f3a30), SkBits2Float(0x49484801), SkBits2Float(0x7d0100c8));  // 0.195576f, 1.67397e-25f, 820352, 1.07172e+37f
 path.conicTo(SkBits2Float(0xff7f36fd), SkBits2Float(0x3e647d01), SkBits2Float(0x0c00f430), SkBits2Float(0x486b6448), SkBits2Float(0x00484848));  // -3.39239e+38f, 0.223133f, 9.93424e-32f, 241041, 6.63809e-39f
@@ -10207,11 +10207,11 @@
 // crbug.com/627761
 static void fuzz763_2b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55));  // 4.76191e+16f, 5.7784e-19f
 path.moveTo(SkBits2Float(0x3b21081f), SkBits2Float(0x4b7bc003));  // 0.00245715f, 1.64987e+07f
 path.lineTo(SkBits2Float(0x2a8ced7a), SkBits2Float(0x21081f21));  // 2.50338e-13f, 4.61198e-19f
@@ -10254,7 +10254,7 @@
 
 static void fuzz763_2c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x36344a4a));  // 0, 2.68653e-06f
 path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x364a4a4a), SkBits2Float(0x364a4a4a), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0, 3.01436e-06f, 3.01436e-06f, 0, 0
@@ -10267,7 +10267,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x1931204a), SkBits2Float(0x2ba1a14a), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a08ff), SkBits2Float(0x4a4a4a34), SkBits2Float(0x4a4a4a4a));  // 9.15721e-24f, 1.14845e-12f, 3.31014e+06f, 3.31014e+06f, 3.31432e+06f, 3.31432e+06f
 path.moveTo(SkBits2Float(0x000010a1), SkBits2Float(0x19312000));  // 5.96533e-42f, 9.15715e-24f
@@ -10279,7 +10279,7 @@
 
 static void fuzz763_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a2a291f));  // 0, 5.14279e+25f
 path.cubicTo(SkBits2Float(0x68295b2d), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x68556829));  // 3.19905e+24f, 0, 0, 0, 0, 4.03114e+24f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x68555b2a));  // 0, 4.03018e+24f
@@ -10295,7 +10295,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3ac23a55), SkBits2Float(0x2a292827));  // 0.00148184f, 1.50241e-13f
 path.lineTo(SkBits2Float(0x63962be6), SkBits2Float(0x272a812a));  // 5.54035e+21f, 2.36623e-15f
 
@@ -10305,11 +10305,11 @@
 
 static void fuzz763_7(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x0f2a312a), SkBits2Float(0xc0032108));  // 8.39112e-30f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10392,7 +10392,7 @@
 
 static void fuzz763_10(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68556829));  // 0, 4.03114e+24f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 6.14991e+25f, 0, 0, 6.14991e+25f
@@ -10401,7 +10401,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x5b2d2968), SkBits2Float(0x2a8c8f55));  // 4.87407e+16f, 2.49685e-13f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -10435,11 +10435,11 @@
 
 static void fuzz763_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x2a0f312a), SkBits2Float(0xc0032108));  // 1.2718e-13f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10486,7 +10486,7 @@
 
 static void fuzz763_12(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x6a29082a));  // 0, 5.10868e+25f
 path.conicTo(SkBits2Float(0x6a295ac3), SkBits2Float(0x61bb988e), SkBits2Float(0x6829682d), SkBits2Float(0x5f3ba76a), SkBits2Float(0x42730a87));  // 5.11843e+25f, 4.32567e+20f, 3.20001e+24f, 1.35219e+19f, 60.7603f
@@ -10502,7 +10502,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003), SkBits2Float(0xed237a6a), SkBits2Float(0x2d682967), SkBits2Float(0x2a8c555b));  // 4.61198e-19f, 1.64987e+07f, -3.16213e+27f, 1.31969e-11f, 2.49282e-13f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -10524,11 +10524,11 @@
 
 static void fuzz763_13(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
 path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0x3adf2128), SkBits2Float(0x4f1a3a8a));  // 6.14991e+25f, 75739, 9.4495e-15f, 0.00170234f, 2.58753e+09f
 path.lineTo(SkBits2Float(0x212a8c55), SkBits2Float(0x21081f2a));  // 5.7784e-19f, 4.61198e-19f
@@ -10560,11 +10560,11 @@
 
 static void fuzz763_14(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0xf45b2d29));  // 4.03114e+24f, -6.94598e+31f
 path.moveTo(SkBits2Float(0x1f2a302a), SkBits2Float(0xc8032108));  // 3.60387e-20f, -134276
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf0db684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x302a5b25), SkBits2Float(0xf0685568));  // 3.4979e+24f, -5.43226e+29f, -1.64207e-31f, 5.76527e-19f, 6.19752e-10f, -2.87615e+29f
@@ -10575,11 +10575,11 @@
 
 static void fuzz763_15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x5b292d55), SkBits2Float(0x212a8c55));  // 4.76191e+16f, 5.7784e-19f
 path.moveTo(SkBits2Float(0x0321081f), SkBits2Float(0x6a6b7bc4));  // 4.7323e-37f, 7.11705e+25f
 path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x2c6829c0), SkBits2Float(0x2a8c555b), SkBits2Float(0x081f2a29));  // 5.77848e-19f, 4.7323e-37f, 3.29924e-12f, 2.49282e-13f, 4.78969e-34f
@@ -10630,11 +10630,11 @@
 
 static void fuzz763_16(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c552775), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.46012e+24f, -2.71613e+29f, -1.64208e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10685,11 +10685,11 @@
 
 static void fuzz763_17(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10737,11 +10737,11 @@
 
 static void fuzz763_18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10788,11 +10788,11 @@
 
 static void fuzz763_19(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x21081f21), SkBits2Float(0x4b7bc003));  // 4.61198e-19f, 1.64987e+07f
 path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x69555b2d));  // 9.43289e-15f, 1.61207e+25f
 path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0682955));  // 3.33127e+24f, -2.87402e+29f
@@ -10838,11 +10838,11 @@
 
 static void fuzz763_20(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -10896,7 +10896,7 @@
 
 static void fuzz763_21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x6828c6f9), SkBits2Float(0x6614dc9e));  // 3.18811e+24f, 1.75745e+23f
 path.cubicTo(SkBits2Float(0x68303469), SkBits2Float(0x661f92fc), SkBits2Float(0x6837d3c3), SkBits2Float(0x662b0eb2), SkBits2Float(0x683fa268), SkBits2Float(0x663759e1));  // 3.32841e+24f, 1.88392e+23f, 3.4724e+24f, 2.01949e+23f, 3.61987e+24f, 2.16463e+23f
 path.cubicTo(SkBits2Float(0x68c4391f), SkBits2Float(0x672c5c9f), SkBits2Float(0x688b20ab), SkBits2Float(0x6804b825), SkBits2Float(0x681ddb5e), SkBits2Float(0x6838dc00));  // 7.4131e+24f, 8.13956e+23f, 5.25609e+24f, 2.507e+24f, 2.98183e+24f, 3.49189e+24f
@@ -10931,7 +10931,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
@@ -10939,7 +10939,7 @@
 
 static void fuzz763_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x68295b2d));  // 0, 3.19905e+24f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x6a3a7bc0), SkBits2Float(0x00000000));  // 5.63611e+25f, 0
@@ -10954,7 +10954,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x4f9a3a8a), SkBits2Float(0xc28a0d28), SkBits2Float(0x273a3ab3), SkBits2Float(0x8b2a2928));  // 5.17506e+09f, -69.0257f, 2.58445e-15f, -3.27718e-32f
 path.lineTo(SkBits2Float(0x63283ae6), SkBits2Float(0x27282a81));  // 3.1033e+21f, 2.33377e-15f
@@ -10965,11 +10965,11 @@
 
 static void fuzz763_23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x03210c2a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.73276e-37f, 6.14991e+25f
@@ -11006,11 +11006,11 @@
 
 static void fuzz763_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed));  // -9.20431e+27f, -9.20445e+27f
 path.close();
 path.moveTo(SkBits2Float(0xededed02), SkBits2Float(0xedededed));  // -9.20431e+27f, -9.20445e+27f
@@ -11055,7 +11055,7 @@
 
 static void fuzz763_25(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x6a4b7bc4));  // 0, 6.14991e+25f
 path.conicTo(SkBits2Float(0x653140d9), SkBits2Float(0x6a4b4f74), SkBits2Float(0x65906630), SkBits2Float(0x6a25a070), SkBits2Float(0x3f6728a2));  // 5.23159e+22f, 6.14468e+25f, 8.52382e+22f, 5.00576e+25f, 0.902964f
 path.cubicTo(SkBits2Float(0x68295bc5), SkBits2Float(0x00000000), SkBits2Float(0x682958ff), SkBits2Float(0x00000000), SkBits2Float(0x68286829), SkBits2Float(0x00000000));  // 3.19909e+24f, 0, 3.19889e+24f, 0, 3.18112e+24f, 0
@@ -11071,7 +11071,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
@@ -11080,11 +11080,11 @@
 
 static void fuzz763_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc003210a));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68372d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.46012e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -11138,11 +11138,11 @@
 
 static void fuzz763_28(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68302d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a1f2a), SkBits2Float(0x0321082a), SkBits2Float(0x6aa37bc0));  // 3.32789e+24f, -2.71613e+29f, -1.64207e-31f, 5.76395e-19f, 4.7323e-37f, 9.88197e+25f
@@ -11176,11 +11176,11 @@
 
 static void fuzz763_27(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x30309ab8), SkBits2Float(0x305b3030), SkBits2Float(0x00f53030), SkBits2Float(0x3a3a0000));  // 6.42483e-10f, 7.97402e-10f, 2.2517e-38f, 0.000709534f
 path.quadTo(SkBits2Float(0xb8b8d5b8), SkBits2Float(0x0b0b0b03), SkBits2Float(0x0b0b0b0b), SkBits2Float(0x3a3a0b0b));  // -8.81361e-05f, 2.67787e-32f, 2.67787e-32f, 0.000709698f
@@ -11192,7 +11192,7 @@
 
 static void fuzz763_29(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x743e0000));  // 0, 6.02134e+31f
 path.cubicTo(SkBits2Float(0x74083cf1), SkBits2Float(0x74536e73), SkBits2Float(0x742ac4e4), SkBits2Float(0x7415f5be), SkBits2Float(0x7433ee3c), SkBits2Float(0x7405a69a));  // 4.31756e+31f, 6.70053e+31f, 5.41189e+31f, 4.75242e+31f, 5.70223e+31f, 4.23556e+31f
@@ -11209,7 +11209,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0xf0682955), SkBits2Float(0x211f5b2d));  // -2.87402e+29f, 5.3992e-19f
 path.moveTo(SkBits2Float(0x2d2aff2d), SkBits2Float(0x74747474));  // 9.72004e-12f, 7.74708e+31f
@@ -11224,11 +11224,11 @@
 
 static void fuzz763_30(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x1f2108c0), SkBits2Float(0x4b7b0321));  // 3.41003e-20f, 1.64503e+07f
 path.lineTo(SkBits2Float(0x6829ed27), SkBits2Float(0x2d555b2d));  // 3.20982e+24f, 1.21279e-11f
 path.moveTo(SkBits2Float(0x68305b2d), SkBits2Float(0xf0685527));  // 3.33127e+24f, -2.87614e+29f
@@ -11254,11 +11254,11 @@
 
 static void fuzz763_31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0xd72a8c55), SkBits2Float(0x61081f2a));  // -1.8752e+14f, 1.56938e+20f
 path.conicTo(SkBits2Float(0x6a4b7bc0), SkBits2Float(0x4793ed7a), SkBits2Float(0x282a3a21), SkBits2Float(0xdf3a2128), SkBits2Float(0x471ac575));  // 6.14991e+25f, 75739, 9.4495e-15f, -1.3412e+19f, 39621.5f
 path.lineTo(SkBits2Float(0x28404040), SkBits2Float(0x552a298a));  // 1.06721e-14f, 1.16935e+13f
@@ -11279,7 +11279,7 @@
 
 static void fuzz763_33(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x72c185d5), SkBits2Float(0x72c184e8));  // 7.66623e+30f, 7.66608e+30f
 path.quadTo(SkBits2Float(0x724341bf), SkBits2Float(0x72433fc4), SkBits2Float(0x6d757575), SkBits2Float(0x6d6d6d6d));  // 3.86746e+30f, 3.86731e+30f, 4.74786e+27f, 4.59251e+27f
 path.cubicTo(SkBits2Float(0x6d18b5e5), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6cbe03bd), SkBits2Float(0x6d4b455b), SkBits2Float(0x6c6c69d8), SkBits2Float(0x6d20df31));  // 2.95385e+27f, 4.59251e+27f, 1.83771e+27f, 3.93183e+27f, 1.14323e+27f, 3.11171e+27f
@@ -11298,7 +11298,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x6c6b6ba7), SkBits2Float(0x886b6b6b));  // 1.13842e+27f, -7.0844e-34f
 path.quadTo(SkBits2Float(0x0000206b), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d), SkBits2Float(0x6d6d6d6d));  // 1.16294e-41f, 4.59251e+27f, 4.59251e+27f, 4.59251e+27f
@@ -11326,11 +11326,11 @@
 
 static void fuzz763_32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0xdedcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0xdcdcdcdc), SkBits2Float(0x55dcdcdc), SkBits2Float(0x29407d7f));  // -7.95742e+18f, -4.97339e+17f, -4.97339e+17f, -4.97339e+17f, 3.03551e+13f, 4.27414e-14f
 path.cubicTo(SkBits2Float(0x7b93ed4b), SkBits2Float(0x29521472), SkBits2Float(0xdfc83c28), SkBits2Float(0x1a3a834e), SkBits2Float(0x6855e84f), SkBits2Float(0xf2f22a80));  // 1.53616e+36f, 4.66471e-14f, -2.88569e+19f, 3.857e-23f, 4.0406e+24f, -9.59318e+30f
@@ -11346,7 +11346,7 @@
 
 static void fuzz763_34(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x63a95a6c), SkBits2Float(0x6cc8e7e2));  // 6.24803e+21f, 1.94304e+27f
 path.quadTo(SkBits2Float(0x63690f37), SkBits2Float(0x6d0a3d9b), SkBits2Float(0x00000000), SkBits2Float(0x6d3e3e3e));  // 4.29919e+21f, 2.67396e+27f, 0, 3.67984e+27f
 path.conicTo(SkBits2Float(0x6b9253fc), SkBits2Float(0x6c956a8b), SkBits2Float(0x6c6ac798), SkBits2Float(0x692a5d27), SkBits2Float(0x3e56eb72));  // 3.538e+26f, 1.44506e+27f, 1.13532e+27f, 1.28723e+25f, 0.209883f
@@ -11364,7 +11364,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x6c3e3e3e), SkBits2Float(0x586c79ff));  // 9.19959e+26f, 1.04003e+15f
 path.quadTo(SkBits2Float(0x6c6c4a6c), SkBits2Float(0x6c6c6c6c), SkBits2Float(0xc83e6c6c), SkBits2Float(0x3e313e3e));  // 1.14263e+27f, 1.14327e+27f, -194994, 0.173089f
 
@@ -11374,11 +11374,11 @@
 
 static void fuzz763_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68556829), SkBits2Float(0x555b2d29));  // 4.03114e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a312a), SkBits2Float(0xc0032108));  // 3.60396e-20f, -2.04889f
 path.cubicTo(SkBits2Float(0x68392d55), SkBits2Float(0xf05b684b), SkBits2Float(0x8c55272d), SkBits2Float(0x212a292a), SkBits2Float(0x0321082a), SkBits2Float(0x6a4b7bc0));  // 3.4979e+24f, -2.71613e+29f, -1.64207e-31f, 5.76527e-19f, 4.7323e-37f, 6.14991e+25f
@@ -11428,11 +11428,11 @@
 
 static void fuzz763_35(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x2aed2a8c), SkBits2Float(0x03210a1f));  // 4.21292e-13f, 4.73253e-37f
 path.conicTo(SkBits2Float(0x0000007b), SkBits2Float(0x7474747f), SkBits2Float(0x74747474), SkBits2Float(0x747474c4), SkBits2Float(0x74747474));  // 1.7236e-43f, 7.74709e+31f, 7.74708e+31f, 7.74712e+31f, 7.74708e+31f
 path.quadTo(SkBits2Float(0x74747474), SkBits2Float(0x74747474), SkBits2Float(0x20437474), SkBits2Float(0x43a52b02));  // 7.74708e+31f, 7.74708e+31f, 1.65557e-19f, 330.336f
@@ -11467,7 +11467,7 @@
 
 static void fuzz763_37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x5b2d3368));  // 1.59583e+13f, 4.87517e+16f
 path.conicTo(SkBits2Float(0x5b2d555b), SkBits2Float(0x68275b2d), SkBits2Float(0x21685527), SkBits2Float(0x0321082a), SkBits2Float(0x6ab485c0));  // 4.8789e+16f, 3.16127e+24f, 7.87174e-19f, 4.7323e-37f, 1.09119e+26f
 path.conicTo(SkBits2Float(0x212a8ced), SkBits2Float(0x0321081f), SkBits2Float(0x6a4b7bc0), SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 5.77848e-19f, 4.7323e-37f, 6.14991e+25f, 9.43289e-15f, 4.8745e+16f
@@ -11511,7 +11511,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0xbcb4bcac), SkBits2Float(0x000029ff), SkBits2Float(0x010000bc), SkBits2Float(0x00bcbc00), SkBits2Float(0xbebcbcbc), SkBits2Float(0xb6aebcae));  // -0.0220626f, 1.50654e-41f, 2.35104e-38f, 1.73325e-38f, -0.368627f, -5.20757e-06f
 
@@ -11521,7 +11521,7 @@
 
 static void fuzz763_38(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0x5b292d11), SkBits2Float(0x212a8c55), SkBits2Float(0x555b2d2d), SkBits2Float(0x52525268));  // 6.53477e+16f, 4.76188e+16f, 5.7784e-19f, 1.50617e+13f, 2.25831e+11f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -11575,7 +11575,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x52528c55), SkBits2Float(0x29215252));  // 2.26074e+11f, 3.58206e-14f
 
     SkPath path2(path);
@@ -11584,11 +11584,11 @@
 
 static void fuzz763_41(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089));  // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f
 path.quadTo(SkBits2Float(0x00057272), SkBits2Float(0x72ff0000), SkBits2Float(0xba405e72), SkBits2Float(0x031b0074));  // 5.00233e-40f, 1.01016e+31f, -0.000733829f, 4.55509e-37f
@@ -11623,11 +11623,11 @@
 
 static void fuzz763_40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x10190004), SkBits2Float(0x7272727a));  // 3.01739e-29f, 4.80216e+30f
 path.quadTo(SkBits2Float(0xf3db5e64), SkBits2Float(0x5b97fc16), SkBits2Float(0x000039fc), SkBits2Float(0x01008000));  // -3.47604e+31f, 8.55598e+16f, 2.08009e-41f, 2.36017e-38f
 path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x41720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 15.1251f
@@ -11650,11 +11650,11 @@
 
 static void fuzz763_39(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x7a057c72), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x055f0089));  // 1.73275e+35f, 4.80216e+30f, 4.34505e+30f, 1.04855e-35f
 path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074));  // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f
@@ -11674,11 +11674,11 @@
 
 static void fuzz763_42(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0x725b5e72), SkBits2Float(0x05720089));  // 1.73224e+35f, 4.80216e+30f, 4.34505e+30f, 1.13789e-35f
 path.quadTo(SkBits2Float(0x7a057272), SkBits2Float(0x72727272), SkBits2Float(0xba405e72), SkBits2Float(0x03000074));  // 1.73224e+35f, 4.80216e+30f, -0.000733829f, 3.76163e-37f
@@ -11705,11 +11705,11 @@
 
 static void fuzz763_43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x5c386c3a), SkBits2Float(0x4e691a3e));  // 2.07642e+17f, 9.77703e+08f
 path.cubicTo(SkBits2Float(0x6f69f9f5), SkBits2Float(0x18ff8791), SkBits2Float(0x2492263c), SkBits2Float(0xbc6fdb48), SkBits2Float(0xc2f82107), SkBits2Float(0x729a18e1));  // 7.24122e+28f, 6.60528e-24f, 6.33822e-17f, -0.0146397f, -124.065f, 6.10442e+30f
 path.cubicTo(SkBits2Float(0x07d729d1), SkBits2Float(0xdea6db48), SkBits2Float(0xcd1dfb88), SkBits2Float(0x90826769), SkBits2Float(0x1c20e5a4), SkBits2Float(0xa4c3ba9b));  // 3.23742e-34f, -6.01164e+18f, -1.65657e+08f, -5.14353e-29f, 5.32364e-22f, -8.48839e-17f
@@ -11740,7 +11740,7 @@
 
 static void fuzz763_44(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x7c223bab), SkBits2Float(0x7cf35966));  // 3.36945e+36f, 1.01083e+37f
 path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0x7ccaca6d), SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 8.4236e+36f, 0, 0
 path.lineTo(SkBits2Float(0x7d7d7d7d), SkBits2Float(0x00000000));  // 2.10591e+37f, 0
@@ -11749,7 +11749,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x109d0000), SkBits2Float(0xff7bc000));  // 6.19256e-29f, -3.34633e+38f
 path.conicTo(SkBits2Float(0x979797ed), SkBits2Float(0x3a214797), SkBits2Float(0x28aa217a), SkBits2Float(0x01007272), SkBits2Float(0x00000072));  // -9.7965e-25f, 0.000615233f, 1.88883e-14f, 2.3592e-38f, 1.59748e-43f
 path.quadTo(SkBits2Float(0x72728302), SkBits2Float(0x8b727272), SkBits2Float(0x72727272), SkBits2Float(0xc00308f6));  // 4.80344e+30f, -4.66936e-32f, 4.80216e+30f, -2.04742f
@@ -11761,11 +11761,11 @@
 
 static void fuzz763_45(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x30303030), SkBits2Float(0x7a303030), SkBits2Float(0x7a303030), SkBits2Float(0x30303030));  // 6.40969e-10f, 6.40969e-10f, 6.40969e-10f, 2.28705e+35f, 2.28705e+35f, 6.40969e-10f
 path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x74303030), SkBits2Float(0x74303030), SkBits2Float(0x30303030), SkBits2Float(0x74303030));  // 6.40969e-10f, 5.58363e+31f, 5.58363e+31f, 6.40969e-10f, 5.58363e+31f
@@ -11796,11 +11796,11 @@
 
 static void fuzz763_46(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
     path.conicTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444), SkBits2Float(0x44263030), SkBits2Float(0x44304430), SkBits2Float(0x4c444430));  // 785.067f, 785.067f, 664.753f, 705.065f, 5.145e+07f
 path.moveTo(SkBits2Float(0x44444444), SkBits2Float(0x44444444));  // 785.067f, 785.067f
@@ -11811,11 +11811,11 @@
 
 static void fuzz763_47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x7272728e), SkBits2Float(0x52527272), SkBits2Float(0x2d555252), SkBits2Float(0x68556829), SkBits2Float(0x555b2d29), SkBits2Float(0x2a212a8c));  // 4.80217e+30f, 2.25966e+11f, 1.21259e-11f, 4.03114e+24f, 1.50617e+13f, 1.43144e-13f
 path.conicTo(SkBits2Float(0x00296808), SkBits2Float(0x00000002), SkBits2Float(0x52525252), SkBits2Float(0x72007272), SkBits2Float(0x52527272));  // 3.80257e-39f, 2.8026e-45f, 2.25831e+11f, 2.54416e+30f, 2.25966e+11f
@@ -11836,11 +11836,11 @@
 
 static void fuzz763_48(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0xed0081bc), SkBits2Float(0x1b2d8040));  // -2.48568e+27f, 1.43517e-22f
 path.moveTo(SkBits2Float(0x74747403), SkBits2Float(0x29747474));  // 7.74703e+31f, 5.42799e-14f
@@ -11874,11 +11874,11 @@
 
 static void fuzz763_49(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x30303030), SkBits2Float(0x78303030), SkBits2Float(0x78787881), SkBits2Float(0x78787878), SkBits2Float(0x30303030));  // 6.40969e-10f, 1.42941e+34f, 2.01583e+34f, 2.01583e+34f, 6.40969e-10f
 path.lineTo(SkBits2Float(0x78787878), SkBits2Float(0x78787878));  // 2.01583e+34f, 2.01583e+34f
@@ -11907,7 +11907,7 @@
 
 static void fuzz763_50(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x70621ede), SkBits2Float(0x00000000));  // 2.79924e+29f, 0
 path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x74fc5b97), SkBits2Float(0x7d458fe4));  // 0, 0, 0, 1.59951e+32f, 1.64128e+37f
 path.lineTo(SkBits2Float(0xefea1ffe), SkBits2Float(0x00000000));  // -1.44916e+29f, 0
@@ -11920,7 +11920,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
@@ -11928,11 +11928,11 @@
 
 static void fuzz763_51(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x868b5aae), SkBits2Float(0x626c45ab), SkBits2Float(0xefea1ffe), SkBits2Float(0x0029fc76));  // -5.24192e-35f, 1.08961e+21f, -1.44916e+29f, 3.85582e-39f
 path.moveTo(SkBits2Float(0xfacbff01), SkBits2Float(0x56fc5b97));  // -5.29604e+35f, 1.38735e+14f
@@ -11944,11 +11944,11 @@
 
 static void fuzz763_52(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x29ff4bae), SkBits2Float(0xa1d75590), SkBits2Float(0x9fd6f6c3), SkBits2Float(0x70621ede));  // 1.13374e-13f, -1.45916e-18f, -9.10408e-20f, 2.79924e+29f
 path.quadTo(SkBits2Float(0x57a839d3), SkBits2Float(0x1a80d34b), SkBits2Float(0x0147a31b), SkBits2Float(0xff7fffff));  // 3.69933e+14f, 5.32809e-23f, 3.66675e-38f, -3.40282e+38f
@@ -11973,7 +11973,7 @@
 
 static void fuzz763_53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x7644b829), SkBits2Float(0x00000000));  // 9.97486e+32f, 0
 path.lineTo(SkBits2Float(0x74fc5b97), SkBits2Float(0x77df944a));  // 1.59951e+32f, 9.06945e+33f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf8fbe3ff));  // 0, -4.08716e+34f
@@ -11982,7 +11982,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x45ab86ae), SkBits2Float(0xd6d6626c), SkBits2Float(0xd6d6d6d6), SkBits2Float(0x7644d6d6));  // 5488.83f, -1.17859e+14f, -1.18109e+14f, 9.98093e+32f
 path.moveTo(SkBits2Float(0xd6d6d6d6), SkBits2Float(0xd6d6d6d6));  // -1.18109e+14f, -1.18109e+14f
@@ -12003,7 +12003,7 @@
 // hangs 654939
 static void fuzz763_54(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x212a8c55));  // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 5.7784e-19f
 path.conicTo(SkBits2Float(0x68555b2d), SkBits2Float(0x28296869), SkBits2Float(0x5b252a08), SkBits2Float(0x5d68392a), SkBits2Float(0x29282780));  // 4.03018e+24f, 9.40402e-15f, 4.64896e+16f, 1.04584e+18f, 3.73378e-14f
@@ -12036,7 +12036,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 4, filename);
@@ -12046,7 +12046,7 @@
 // afl crash
 static void fuzz763_55(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x55415500));  // 0, 1.32857e+13f
 path.lineTo(SkBits2Float(0x55555568), SkBits2Float(0x55555555));  // 1.46602e+13f, 1.46602e+13f
 path.lineTo(SkBits2Float(0x98989898), SkBits2Float(0x55989898));  // -3.94452e-24f, 2.09726e+13f
@@ -12067,7 +12067,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 3, filename);
@@ -12076,7 +12076,7 @@
 // 656149
 static void fuzz763_56(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x5b682968), SkBits2Float(0xb3b32d11), SkBits2Float(0xb3b3b3b3), SkBits2Float(0x5b29b3b3), SkBits2Float(0x72725255));  // 6.53477e+16f, -8.34353e-08f, -8.36802e-08f, 4.77669e+16f, 4.79967e+30f
 path.quadTo(SkBits2Float(0x525252c7), SkBits2Float(0x72725252), SkBits2Float(0x72727272), SkBits2Float(0x72727255));  // 2.25833e+11f, 4.79967e+30f, 4.80216e+30f, 4.80215e+30f
@@ -12105,7 +12105,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 0, filename);
@@ -12113,11 +12113,11 @@
 
 static void fuzz763_57(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x68546829), SkBits2Float(0x555b2d29));  // 4.01225e+24f, 1.50617e+13f
 path.moveTo(SkBits2Float(0x1f2a322a), SkBits2Float(0x4b7b2108));  // 3.60404e-20f, 1.6458e+07f
 path.lineTo(SkBits2Float(0x2829ed84), SkBits2Float(0x5b2d2d55));  // 9.43289e-15f, 4.8745e+16f
@@ -12162,7 +12162,7 @@
 
 static void fuzzhang_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x668ece09), SkBits2Float(0x00000000), SkBits2Float(0x6751c81a), SkBits2Float(0x61c4b0fb));  // 0, 0, 3.37188e+23f, 0, 9.90666e+23f, 4.53539e+20f
 path.conicTo(SkBits2Float(0x66f837a9), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x3f823406));  // 5.86087e+23f, 0, 0, 0, 1.01721f
@@ -12177,7 +12177,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x535353ec), SkBits2Float(0x98989898), SkBits2Float(0x98989898), SkBits2Float(0xf207f36e), SkBits2Float(0xf3f2f2f2), SkBits2Float(0xed3a9781));  // 9.07646e+11f, -3.94452e-24f, -3.94452e-24f, -2.69278e+30f, -3.84968e+31f, -3.60921e+27f
 path.quadTo(SkBits2Float(0xf8f8c0ed), SkBits2Float(0xf8f8f8f8), SkBits2Float(0x9f9f9f9f), SkBits2Float(0x3014149f));  // -4.03626e+34f, -4.03981e+34f, -6.76032e-20f, 5.38714e-10f
@@ -12188,8 +12188,8 @@
 
 static void release_13(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
-path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType((SkPathFillType) 1);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xd4438848), SkBits2Float(0xd488cf64));  // -3.35922e+12f, -4.70076e+12f
 path.lineTo(SkBits2Float(0xd43a056e), SkBits2Float(0xd4851696));  // -3.19582e+12f, -4.57288e+12f
 path.quadTo(SkBits2Float(0xd3d48e79), SkBits2Float(0xd49fb136), SkBits2Float(0x00000000), SkBits2Float(0xd4d4d4d4));  // -1.82585e+12f, -5.48698e+12f, 0, -7.31283e+12f
@@ -12246,8 +12246,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.quadTo(SkBits2Float(0x00000000), SkBits2Float(0xa5a50000), SkBits2Float(0xd4d4a5a5), SkBits2Float(0xd4d4d4d4));  // 0, -2.86229e-16f, -7.3065e+12f, -7.31283e+12f
 path.quadTo(SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4d4d4d4), SkBits2Float(0xd4cfd4d4), SkBits2Float(0xd4d41dd4));  // -7.31283e+12f, -7.31283e+12f, -7.14103e+12f, -7.28827e+12f
@@ -12264,8 +12264,8 @@
 
 static void fuzzhang_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f
 path.quadTo(SkBits2Float(0xe0e02972), SkBits2Float(0xe0e060e0), SkBits2Float(0x728e4603), SkBits2Float(0x72727272));  // -1.29221e+20f, -1.29345e+20f, 5.63603e+30f, 4.80216e+30f
 path.lineTo(SkBits2Float(0x5568392a), SkBits2Float(0x72837268));  // 1.59583e+13f, 5.20715e+30f
@@ -12293,8 +12293,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 
     SkPath path2(path);
     testPathOpFuzz(reporter, path1, path2, (SkPathOp) 1, filename);
@@ -12302,13 +12302,13 @@
 
 static void fuzzhang_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x46090052), SkBits2Float(0x7270726c));  // 8768.08f, 4.76254e+30f
 path.moveTo(SkBits2Float(0xe0437272), SkBits2Float(0x03e0e060));  // -5.63338e+19f, 1.32171e-36f
 path.close();
@@ -12354,13 +12354,13 @@
 
 static void fuzz754434_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x535e5372), SkBits2Float(0x53536153), SkBits2Float(0x79530f53), SkBits2Float(0x101b6c88), SkBits2Float(0x5353735e), SkBits2Float(0x006df653));  // 9.54883e+11f, 9.07871e+11f, 6.84928e+34f, 3.0652e-29f, 9.08174e+11f, 1.00984e-38f
 path.cubicTo(SkBits2Float(0xf26df46d), SkBits2Float(0xf6f6f6f6), SkBits2Float(0x5656f666), SkBits2Float(0x5a565656), SkBits2Float(0x00000056), SkBits2Float(0xf66e5600));  // -4.71318e+30f, -2.50452e+33f, 5.90884e+13f, 1.50826e+16f, 1.20512e-43f, -1.20851e+33f
@@ -12374,8 +12374,8 @@
 
 static void fuzz754434_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
-path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType((SkPathFillType) 1);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xff00ff56), SkBits2Float(0x00000000));  // -1.71467e+38f, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf66e5600));  // 0, -1.20851e+33f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf629168b));  // 0, -8.57378e+32f
@@ -12385,8 +12385,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x03e8f6f6), SkBits2Float(0xf7060000));  // 1.36924e-36f, -2.71784e+33f
 path.lineTo(SkBits2Float(0x4ff6f6f6), SkBits2Float(0x3e3e3e2a));  // 8.28676e+09f, 0.185784f
@@ -12398,13 +12398,13 @@
 
 static void fuzz754434_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x535e5372), SkBits2Float(0x53536153), SkBits2Float(0x79530f53), SkBits2Float(0x101b6c88), SkBits2Float(0x5353735e), SkBits2Float(0x006df653));  // 9.54883e+11f, 9.07871e+11f, 6.84928e+34f, 3.0652e-29f, 9.08174e+11f, 1.00984e-38f
 path.cubicTo(SkBits2Float(0xf26df46d), SkBits2Float(0xf6f6f6f6), SkBits2Float(0x5656f666), SkBits2Float(0x5a565656), SkBits2Float(0x00000056), SkBits2Float(0xf66e5600));  // -4.71318e+30f, -2.50452e+33f, 5.90884e+13f, 1.50826e+16f, 1.20512e-43f, -1.20851e+33f
@@ -12418,8 +12418,8 @@
 
 static void fuzz754434_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
-path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType((SkPathFillType) 1);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(SkBits2Float(0xff00ff56), SkBits2Float(0x00000000));  // -1.71467e+38f, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf66e5600));  // 0, -1.20851e+33f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0xf629168b));  // 0, -8.57378e+32f
@@ -12429,8 +12429,8 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
-path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType((SkPathFillType) 0);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x03e8f6f6), SkBits2Float(0xf7060000));  // 1.36924e-36f, -2.71784e+33f
 path.lineTo(SkBits2Float(0x4ff6f6f6), SkBits2Float(0x3e3e3e2a));  // 8.28676e+09f, 0.185784f
diff --git a/tests/PathOpsSimplifyDegenerateThreadedTest.cpp b/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
index cf36924..7a94d07 100644
--- a/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
+++ b/tests/PathOpsSimplifyDegenerateThreadedTest.cpp
@@ -32,7 +32,6 @@
                 }
                 SkString pathStr;
                 SkPath path, out;
-                path.setFillType(SkPath::kWinding_FillType);
                 path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
                 path.lineTo(SkIntToScalar(bx), SkIntToScalar(by));
                 path.lineTo(SkIntToScalar(cx), SkIntToScalar(cy));
@@ -50,12 +49,12 @@
                     pathStr.appendf("    path.lineTo(%d, %d);\n", ex, ey);
                     pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
                     pathStr.appendf("    path.close();\n");
-                    state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
+                    state.outputProgress(pathStr.c_str(), SkPathFillType::kWinding);
                 }
                 testSimplify(path, false, out, state, pathStr.c_str());
-                path.setFillType(SkPath::kEvenOdd_FillType);
+                path.setFillType(SkPathFillType::kEvenOdd);
                 if (state.fReporter->verbose()) {
-                    state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
+                    state.outputProgress(pathStr.c_str(), SkPathFillType::kEvenOdd);
                 }
                 testSimplify(path, true, out, state, pathStr.c_str());
             }
diff --git a/tests/PathOpsSimplifyFailTest.cpp b/tests/PathOpsSimplifyFailTest.cpp
index 6e7c650..7503c55 100644
--- a/tests/PathOpsSimplifyFailTest.cpp
+++ b/tests/PathOpsSimplifyFailTest.cpp
@@ -59,11 +59,11 @@
         case 12: path.moveTo(nonFinitePts[i]); break;
     }
     SkPath result;
-    result.setFillType(SkPath::kWinding_FillType);
+    result.setFillType(SkPathFillType::kWinding);
     bool success = Simplify(path, &result);
     REPORTER_ASSERT(reporter, !success);
     REPORTER_ASSERT(reporter, result.isEmpty());
-    REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
+    REPORTER_ASSERT(reporter, result.getNewFillType() == SkPathFillType::kWinding);
     reporter->bumpTestCount();
 }
 
@@ -85,10 +85,10 @@
         case 10: path.moveTo(finitePts[f]); break;
     }
     SkPath result;
-    result.setFillType(SkPath::kWinding_FillType);
+    result.setFillType(SkPathFillType::kWinding);
     bool success = Simplify(path, &result);
     REPORTER_ASSERT(reporter, success);
-    REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
+    REPORTER_ASSERT(reporter, result.getNewFillType() != SkPathFillType::kWinding);
     reporter->bumpTestCount();
 }
 
@@ -126,7 +126,6 @@
 
 static void fuzz763_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0xbcb63000), SkBits2Float(0xb6b6b6b7), SkBits2Float(0x38b6b6b6), SkBits2Float(0xafb63a5a), SkBits2Float(0xca000087), SkBits2Float(0xe93ae9e9));  // -0.0222397f, -5.44529e-06f, 8.71247e-05f, -3.31471e-10f, -2.09719e+06f, -1.41228e+25f
 path.quadTo(SkBits2Float(0xb6007fb6), SkBits2Float(0xb69fb6b6), SkBits2Float(0xe9e964b6), SkBits2Float(0xe9e9e9e9));  // -1.91478e-06f, -4.75984e-06f, -3.52694e+25f, -3.5348e+25f
@@ -157,7 +156,6 @@
 
 static void fuzz763_2s(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x76773011), SkBits2Float(0x5d66fe78), SkBits2Float(0xbbeeff66), SkBits2Float(0x637677a2), SkBits2Float(0x205266fe), SkBits2Float(0xec296fdf));  // 1.25339e+33f, 1.0403e+18f, -0.00729363f, 4.54652e+21f, 1.78218e-19f, -8.19347e+26f
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
@@ -217,7 +215,6 @@
 
 static void fuzz_x3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kWinding_FillType);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.cubicTo(SkBits2Float(0x92743420), SkBits2Float(0x74747474), SkBits2Float(0x0f747c74), SkBits2Float(0xff538565), SkBits2Float(0x74744374), SkBits2Float(0x20437474));  // -7.70571e-28f, 7.74708e+31f, 1.20541e-29f, -2.8116e+38f, 7.74102e+31f, 1.65557e-19f
 path.conicTo(SkBits2Float(0x7474926d), SkBits2Float(0x7c747474), SkBits2Float(0x00170f74), SkBits2Float(0x3a7410d7), SkBits2Float(0x3a3a3a3a));  // 7.7508e+31f, 5.07713e+36f, 2.11776e-39f, 0.000931037f, 0.000710401f
@@ -229,7 +226,6 @@
 
 static void fuzz_k1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kWinding_FillType);
 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.conicTo(SkBits2Float(0x2073732f), SkBits2Float(0x73f17f00), SkBits2Float(0x737b7b73), SkBits2Float(0x73916773), SkBits2Float(0x00738773));  // 2.0621e-19f, 3.82666e+31f, 1.99245e+31f, 2.30402e+31f, 1.06097e-38f
 path.lineTo(SkBits2Float(0x5803736d), SkBits2Float(0x807b5ba1));  // 5.78127e+14f, -1.13286e-38f
diff --git a/tests/PathOpsSimplifyQuadThreadedTest.cpp b/tests/PathOpsSimplifyQuadThreadedTest.cpp
index fc5f646..52fa8fc 100644
--- a/tests/PathOpsSimplifyQuadThreadedTest.cpp
+++ b/tests/PathOpsSimplifyQuadThreadedTest.cpp
@@ -36,7 +36,6 @@
                     int hx = h & 0x03;
                     int hy = h >> 2;
                     SkPath path, out;
-                    path.setFillType(SkPath::kWinding_FillType);
                     path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
                     path.quadTo(SkIntToScalar(bx), SkIntToScalar(by),
                             SkIntToScalar(cx), SkIntToScalar(cy));
@@ -61,12 +60,12 @@
                         pathStr.appendf("    path.close();\n");
                         pathStr.appendf("    testSimplify(reporter, path, filename);\n");
                         pathStr.appendf("}\n");
-                        state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
+                        state.outputProgress(pathStr.c_str(), SkPathFillType::kWinding);
                     }
                     testSimplify(path, false, out, state, pathStr.c_str());
-                    path.setFillType(SkPath::kEvenOdd_FillType);
+                    path.setFillType(SkPathFillType::kEvenOdd);
                     if (state.fReporter->verbose()) {
-                        state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
+                        state.outputProgress(pathStr.c_str(), SkPathFillType::kEvenOdd);
                     }
                     testSimplify(path, true, out, state, pathStr.c_str());
                 }
diff --git a/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp b/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
index 2cfca49..ecb4f29 100644
--- a/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
+++ b/tests/PathOpsSimplifyQuadralateralsThreadedTest.cpp
@@ -36,7 +36,6 @@
                     int hx = h & 0x03;
                     int hy = h >> 2;
                     SkPath path, out;
-                    path.setFillType(SkPath::kWinding_FillType);
                     path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
                     path.lineTo(SkIntToScalar(bx), SkIntToScalar(by));
                     path.lineTo(SkIntToScalar(cx), SkIntToScalar(cy));
@@ -63,12 +62,12 @@
                         pathStr.appendf("    path.close();\n");
                         pathStr.appendf("    testPathSimplify(reporter, path, filename);\n");
                         pathStr.appendf("}\n");
-                        state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
+                        state.outputProgress(pathStr.c_str(), SkPathFillType::kWinding);
                     }
                     testSimplify(path, false, out, state, pathStr.c_str());
-                    path.setFillType(SkPath::kEvenOdd_FillType);
+                    path.setFillType(SkPathFillType::kEvenOdd);
                     if (state.fReporter->verbose()) {
-                        state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
+                        state.outputProgress(pathStr.c_str(), SkPathFillType::kEvenOdd);
                     }
                     testSimplify(path, true, out, state, pathStr.c_str());
                 }
diff --git a/tests/PathOpsSimplifyRectThreadedTest.cpp b/tests/PathOpsSimplifyRectThreadedTest.cpp
index e3b58a3..588b6b3 100644
--- a/tests/PathOpsSimplifyRectThreadedTest.cpp
+++ b/tests/PathOpsSimplifyRectThreadedTest.cpp
@@ -22,13 +22,13 @@
     SkASSERT(data);
     PathOpsThreadState& state = *data;
     int aShape = state.fA & 0x03;
-    SkPath::Direction aCW = state.fA >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+    SkPathDirection aCW = state.fA >> 2 ? SkPathDirection::kCCW : SkPathDirection::kCW;
     int bShape = state.fB & 0x03;
-    SkPath::Direction bCW = state.fB >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+    SkPathDirection bCW = state.fB >> 2 ? SkPathDirection::kCCW : SkPathDirection::kCW;
     int cShape = state.fC & 0x03;
-    SkPath::Direction cCW = state.fC >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+    SkPathDirection cCW = state.fC >> 2 ? SkPathDirection::kCCW : SkPathDirection::kCW;
     int dShape = state.fD & 0x03;
-    SkPath::Direction dCW = state.fD >> 2 ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+    SkPathDirection dCW = state.fD >> 2 ? SkPathDirection::kCCW : SkPathDirection::kCW;
     for (int aXAlign = 0; aXAlign < 5; ++aXAlign) {
         for (int aYAlign = 0; aYAlign < 5; ++aYAlign) {
             for (int bXAlign = 0; bXAlign < 5; ++bXAlign) {
@@ -39,7 +39,6 @@
     for (int dYAlign = 0; dYAlign < 5; ++dYAlign) {
         SkString pathStr;
         SkPath path, out;
-        path.setFillType(SkPath::kWinding_FillType);
         int l SK_INIT_TO_AVOID_WARNING, t SK_INIT_TO_AVOID_WARNING,
             r SK_INIT_TO_AVOID_WARNING, b SK_INIT_TO_AVOID_WARNING;
         if (aShape) {
@@ -67,7 +66,8 @@
                     aCW);
             if (state.fReporter->verbose()) {
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sW_Direction);\n", l, t, r, b, aCW ? "C" : "");
+                        " SkPathDirection::kC%sW);\n", l, t, r, b,
+                                aCW == SkPathDirection::kCCW ? "C" : "");
             }
         } else {
             aXAlign = 5;
@@ -98,7 +98,8 @@
                     bCW);
             if (state.fReporter->verbose()) {
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sW_Direction);\n", l, t, r, b, bCW ? "C" : "");
+                        " SkPathDirection::kC%sW);\n", l, t, r, b,
+                                bCW == SkPathDirection::kCCW ? "C" : "");
             }
         } else {
             bXAlign = 5;
@@ -129,7 +130,8 @@
                     cCW);
             if (state.fReporter->verbose()) {
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sW_Direction);\n", l, t, r, b, cCW ? "C" : "");
+                        " SkPathDirection::kC%sW);\n", l, t, r, b,
+                                cCW == SkPathDirection::kCCW ? "C" : "");
             }
         } else {
             cXAlign = 5;
@@ -160,7 +162,8 @@
                     dCW);
             if (state.fReporter->verbose()) {
                 pathStr.appendf("    path.addRect(%d, %d, %d, %d,"
-                        " SkPath::kC%sW_Direction);\n", l, t, r, b, dCW ? "C" : "");
+                        " SkPathDirection::kC%sW);\n", l, t, r, b,
+                                dCW == SkPathDirection::kCCW ? "C" : "");
             }
         } else {
             dXAlign = 5;
@@ -168,11 +171,11 @@
         }
         path.close();
         if (state.fReporter->verbose()) {
-            state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
+            state.outputProgress(pathStr.c_str(), SkPathFillType::kWinding);
         }
         testSimplify(path, false, out, state, pathStr.c_str());
         if (state.fReporter->verbose()) {
-            state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
+            state.outputProgress(pathStr.c_str(), SkPathFillType::kEvenOdd);
         }
         testSimplify(path, true, out, state, pathStr.c_str());
     }
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index e95a902..9d8e6b6 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -19,7 +19,7 @@
 
 static void testLine1x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(2,0);
     path.lineTo(1,1);
     path.lineTo(0,0);
@@ -64,7 +64,7 @@
 
 static void testLine2x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addInnerCWTriangle(path);
     addOuterCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -79,7 +79,7 @@
 
 static void testLine3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addInnerCCWTriangle(path);
     addOuterCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -94,7 +94,7 @@
 
 static void testLine3ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addInnerCWTriangle(path);
     addOuterCCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -109,7 +109,7 @@
 
 static void testLine3bx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addInnerCCWTriangle(path);
     addOuterCCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -124,7 +124,7 @@
 
 static void testLine4x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addOuterCCWTriangle(path);
     addOuterCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -139,7 +139,7 @@
 
 static void testLine5x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addOuterCWTriangle(path);
     addOuterCWTriangle(path);
     testSimplify(reporter, path, filename);
@@ -160,7 +160,7 @@
 
 static void testLine6x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(4,0);
     path.lineTo(2,2);
@@ -187,7 +187,7 @@
 
 static void testLine7x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(4,0);
     path.lineTo(2,2);
@@ -210,7 +210,7 @@
 
 static void testLine7ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(4,0);
     path.lineTo(2,2);
@@ -232,7 +232,7 @@
 
 static void testLine7bx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,0);
     path.lineTo(4,0);
     path.close();
@@ -258,7 +258,7 @@
 
 static void testLine8x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,4);
     path.lineTo(4,4);
     path.lineTo(2,2);
@@ -285,7 +285,7 @@
 
 static void testLine9x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,4);
     path.lineTo(4,4);
     path.lineTo(2,2);
@@ -312,7 +312,7 @@
 
 static void testLine10x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,4);
     path.lineTo(4,4);
     path.lineTo(2,2);
@@ -339,7 +339,7 @@
 
 static void testLine10ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0,4);
     path.lineTo(8,4);
     path.lineTo(4,0);
@@ -388,7 +388,7 @@
 
 static void testLine11x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addCWContainer(path);
     addCWContents(path);
     testSimplify(reporter, path, filename);
@@ -403,7 +403,7 @@
 
 static void testLine12x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addCCWContainer(path);
     addCWContents(path);
     testSimplify(reporter, path, filename);
@@ -418,7 +418,7 @@
 
 static void testLine13x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addCWContainer(path);
     addCCWContents(path);
     testSimplify(reporter, path, filename);
@@ -433,7 +433,7 @@
 
 static void testLine14x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     addCCWContainer(path);
     addCCWContents(path);
     testSimplify(reporter, path, filename);
@@ -441,134 +441,134 @@
 
 static void testLine15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine15x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine16(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 4, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 4, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine16x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 4, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 4, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine17(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine17x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 4, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 4, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine18x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 4, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 4, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine19(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 16, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 16, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine19x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 16, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 16, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine20(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine20x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 16, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 16, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine21x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 16, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 16, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine22x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine23x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
@@ -587,7 +587,7 @@
 
 static void testLine24ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(2,0);
     path.lineTo(4,4);
     path.lineTo(0,4);
@@ -601,1081 +601,1081 @@
 
 static void testLine24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine24x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine25(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine25x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine26x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine27(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 8, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 8, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine27x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 8, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 8, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine28(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine28x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine29(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 12, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 12, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine29x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 18, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 12, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 18, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 12, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine30(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine30x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 4, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 4, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine31x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 4, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 4, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine32x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine33(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine33x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine34(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine34x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine35(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 0, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 0, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine35x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 0, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 0, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 10, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 10, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine36x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 10, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 10, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 24, 30, 30, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 24, 30, 30, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine37x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 24, 30, 30, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 24, 30, 30, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine38(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCW_Direction);
-    path.addRect(12, 12, 21, 21, SkPath::kCW_Direction);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCW);
+    path.addRect(12, 12, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine38x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCW_Direction);
-    path.addRect(12, 12, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCW);
+    path.addRect(12, 12, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 18, 24, 24, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 18, 24, 24, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine40x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 18, 24, 24, SkPath::kCW_Direction);
-    path.addRect(4, 16, 13, 13, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 18, 24, 24, SkPathDirection::kCW);
+    path.addRect(4, 16, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine41(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 24, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 24, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine41x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 24, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 24, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine42(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(8, 16, 17, 17, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(8, 16, 17, 17, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine42x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(8, 16, 17, 17, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(8, 16, 17, 17, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 24, 18, 18, SkPath::kCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 24, 18, 18, SkPathDirection::kCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine43x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 24, 18, 18, SkPath::kCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 24, 18, 18, SkPathDirection::kCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine44(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 32, 27, 36, SkPath::kCCW_Direction);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 32, 27, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine44x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 32, 27, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 32, 27, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine45(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCW_Direction);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine45x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine46(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 0, 36, 36, SkPath::kCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCW_Direction);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 0, 36, 36, SkPathDirection::kCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine46x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 0, 36, 36, SkPath::kCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 0, 36, 36, SkPathDirection::kCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine47(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine47x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine48(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine48x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine49(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine49x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine50(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCW_Direction);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine50x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine51(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine51x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine52(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 30, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 20, 18, 30, SkPath::kCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCW_Direction);
+    path.addRect(0, 30, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 20, 18, 30, SkPathDirection::kCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine52x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 30, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 20, 18, 30, SkPath::kCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 30, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 20, 18, 30, SkPathDirection::kCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 20, 24, 30, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCCW_Direction);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 20, 24, 30, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine53x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 20, 24, 30, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 20, 24, 30, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine54(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 0, 18, 18, SkPath::kCW_Direction);
-    path.addRect(8, 4, 17, 17, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 0, 18, 18, SkPathDirection::kCW);
+    path.addRect(8, 4, 17, 17, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine54x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 0, 18, 18, SkPath::kCW_Direction);
-    path.addRect(8, 4, 17, 17, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 0, 18, 18, SkPathDirection::kCW);
+    path.addRect(8, 4, 17, 17, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine55(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 6, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 6, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine55x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 6, 18, 18, SkPath::kCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 6, 18, 18, SkPathDirection::kCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine56(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 20, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 20, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine56x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(18, 20, 30, 30, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(18, 20, 30, 30, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine57(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(20, 0, 40, 40, SkPath::kCW_Direction);
-    path.addRect(20, 0, 30, 40, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCW);
+    path.addRect(20, 0, 30, 40, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine57x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(20, 0, 40, 40, SkPath::kCW_Direction);
-    path.addRect(20, 0, 30, 40, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCW);
+    path.addRect(20, 0, 30, 40, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine58(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine58x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 0, 12, 12, SkPath::kCCW_Direction);
-    path.addRect(0, 12, 9, 9, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCCW);
+    path.addRect(0, 12, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine59(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 6, 18, 18, SkPath::kCCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 6, 18, 18, SkPathDirection::kCCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine59x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 6, 18, 18, SkPath::kCCW_Direction);
-    path.addRect(4, 4, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 6, 18, 18, SkPathDirection::kCCW);
+    path.addRect(4, 4, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine60(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine60x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(6, 12, 18, 18, SkPath::kCCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(6, 12, 18, 18, SkPathDirection::kCCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 0, 24, 24, SkPath::kCCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 0, 24, 24, SkPathDirection::kCCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine61x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 0, 24, 24, SkPath::kCCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 0, 24, 24, SkPathDirection::kCCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine62x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine63(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 10, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 10, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine63x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 10, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 6, 12, 12, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 10, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 6, 12, 12, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine64(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 6, 30, 30, SkPath::kCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 6, 30, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine64x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 6, 30, 30, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 6, 30, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine65(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 0, 36, 36, SkPath::kCW_Direction);
-    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 0, 36, 36, SkPathDirection::kCW);
+    path.addRect(32, 6, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine65x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 0, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 0, 36, 36, SkPath::kCW_Direction);
-    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 0, 36, 36, SkPathDirection::kCW);
+    path.addRect(32, 6, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine66(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 30, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 20, 24, 30, SkPath::kCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 30, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 20, 24, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine66x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 30, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 20, 24, 30, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 30, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 20, 24, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine67(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine67x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68bx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68cx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68dx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 4, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 4, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68e(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68ex(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68f(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68fx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68g(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68gx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68h(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine68hx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 8, 8, SkPath::kCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 6, 6, SkPath::kCCW_Direction);
-    path.addRect(1, 2, 2, 2, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 8, 8, SkPathDirection::kCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(2, 2, 6, 6, SkPathDirection::kCCW);
+    path.addRect(1, 2, 2, 2, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine69(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCW_Direction);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine69x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine70(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 24, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 24, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine70x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 24, 12, 12, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 24, 12, 12, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine71(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 0, 24, 24, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCW_Direction);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 0, 24, 24, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine71x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 20, 20, SkPath::kCW_Direction);
-    path.addRect(12, 0, 24, 24, SkPath::kCW_Direction);
-    path.addRect(12, 32, 21, 36, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 20, 20, SkPathDirection::kCW);
+    path.addRect(12, 0, 24, 24, SkPathDirection::kCW);
+    path.addRect(12, 32, 21, 36, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine72(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(6, 20, 18, 30, SkPath::kCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(6, 20, 18, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine72x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 40, 30, 30, SkPath::kCW_Direction);
-    path.addRect(6, 20, 18, 30, SkPath::kCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 40, 30, 30, SkPathDirection::kCW);
+    path.addRect(6, 20, 18, 30, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine73(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 40, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 40, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine73x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(0, 40, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCW_Direction);
-    path.addRect(0, 0, 9, 9, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(0, 40, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCW);
+    path.addRect(0, 0, 9, 9, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine74(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(20, 30, 40, 40, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(32, 24, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine74x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(20, 30, 40, 40, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(32, 24, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine75(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 0, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine75x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCW_Direction);
-    path.addRect(10, 0, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(18, 0, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCW);
+    path.addRect(10, 0, 30, 30, SkPathDirection::kCCW);
+    path.addRect(18, 0, 30, 30, SkPathDirection::kCCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine76(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(36, 0, 66, 60, SkPath::kCW_Direction);
-    path.addRect(10, 20, 40, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(36, 0, 66, 60, SkPathDirection::kCW);
+    path.addRect(10, 20, 40, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(32, 6, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine76x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(36, 0, 66, 60, SkPath::kCW_Direction);
-    path.addRect(10, 20, 40, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(36, 0, 66, 60, SkPathDirection::kCW);
+    path.addRect(10, 20, 40, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(32, 6, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine77(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(20, 0, 40, 40, SkPath::kCW_Direction);
-    path.addRect(24, 6, 36, 36, SkPath::kCCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCW);
+    path.addRect(24, 6, 36, 36, SkPathDirection::kCCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine77x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(20, 0, 40, 40, SkPath::kCW_Direction);
-    path.addRect(24, 6, 36, 36, SkPath::kCCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCW);
+    path.addRect(24, 6, 36, 36, SkPathDirection::kCCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine78(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 30, 60, SkPath::kCW_Direction);
-    path.addRect(10, 20, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(18, 20, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 30, 60, SkPathDirection::kCW);
+    path.addRect(10, 20, 30, 30, SkPathDirection::kCCW);
+    path.addRect(18, 20, 30, 30, SkPathDirection::kCCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine78x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 30, 60, SkPath::kCW_Direction);
-    path.addRect(10, 20, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(18, 20, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 30, 60, SkPathDirection::kCW);
+    path.addRect(10, 20, 30, 30, SkPathDirection::kCCW);
+    path.addRect(18, 20, 30, 30, SkPathDirection::kCCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine79(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 36, 60, 30, SkPath::kCW_Direction);
-    path.addRect(10, 30, 40, 30, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.addRect(0, 36, 60, 30, SkPathDirection::kCW);
+    path.addRect(10, 30, 40, 30, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine79x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 36, 60, 30, SkPath::kCW_Direction);
-    path.addRect(10, 30, 40, 30, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 36, 60, 30, SkPathDirection::kCW);
+    path.addRect(10, 30, 40, 30, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine81(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(-1, -1, 3, 3, SkPath::kCW_Direction);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(0, 0, 1, 1, SkPath::kCW_Direction);
-    path.addRect(1, 1, 2, 2, SkPath::kCCW_Direction);
+    path.addRect(-1, -1, 3, 3, SkPathDirection::kCW);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(0, 0, 1, 1, SkPathDirection::kCW);
+    path.addRect(1, 1, 2, 2, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
@@ -1694,7 +1694,7 @@
 
 static void testDegenerate1x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(2, 0);
@@ -1721,7 +1721,7 @@
 
 static void testDegenerate2x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(0, 0);
@@ -1748,7 +1748,7 @@
 
 static void testDegenerate3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(2, 0);
     path.lineTo(1, 0);
@@ -1775,7 +1775,7 @@
 
 static void testDegenerate4x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(1, 0);
     path.lineTo(1, 3);
@@ -1802,7 +1802,7 @@
 
 static void testNondegenerate1x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 0);
     path.lineTo(1, 3);
@@ -1829,7 +1829,7 @@
 
 static void testNondegenerate2x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.lineTo(0, 1);
     path.lineTo(1, 1);
@@ -1856,7 +1856,7 @@
 
 static void testNondegenerate3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(1, 0);
     path.lineTo(2, 1);
@@ -1883,7 +1883,7 @@
 
 static void testNondegenerate4x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.lineTo(0, 1);
     path.lineTo(1, 2);
@@ -1912,7 +1912,7 @@
 
 static void testQuadralateral5x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(1, 0);
@@ -1943,7 +1943,7 @@
 
 static void testQuadralateral6x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(1, 0);
@@ -1976,7 +1976,7 @@
 
 static void testFauxQuadralateral6x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(1, 0);
     path.lineTo(1, 1);
@@ -2011,7 +2011,7 @@
 
 static void testFauxQuadralateral6ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 0);
     path.lineTo(3, 3);
@@ -2046,7 +2046,7 @@
 
 static void testFauxQuadralateral6bx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 0);
     path.lineTo(3, 3);
@@ -2081,7 +2081,7 @@
 
 static void testFauxQuadralateral6cx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 3);
     path.lineTo(3, 0);
@@ -2116,7 +2116,7 @@
 
 static void testFauxQuadralateral6dx(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 3);
     path.lineTo(3, 0);
@@ -2149,7 +2149,7 @@
 
 static void testQuadralateral6ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(3, 0);
@@ -2180,7 +2180,7 @@
 
 static void testQuadralateral7x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(1, 0);
@@ -2211,7 +2211,7 @@
 
 static void testQuadralateral8x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(3, 1);
     path.lineTo(1, 3);
@@ -2242,7 +2242,7 @@
 
 static void testQuadralateral9x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(1, 0);
     path.lineTo(1, 2);
@@ -2258,44 +2258,44 @@
 
 static void testLine1a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 0, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kWinding);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 0, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine1ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 0, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 0, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine2ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 20, 20, 20, SkPath::kCW_Direction);
-    path.addRect(0, 20, 12, 30, SkPath::kCW_Direction);
-    path.addRect(12, 0, 21, 21, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 20, 20, 20, SkPathDirection::kCW);
+    path.addRect(0, 20, 12, 30, SkPathDirection::kCW);
+    path.addRect(12, 0, 21, 21, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine3aax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(18, 20, 30, 30, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(18, 20, 30, 30, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine4ax(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(10, 30, 30, 30, SkPath::kCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(0, 32, 9, 36, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(10, 30, 30, 30, SkPathDirection::kCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(0, 32, 9, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
@@ -2314,7 +2314,7 @@
 
 static void testQuadratic1x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(1, 0);
@@ -2341,7 +2341,7 @@
 
 static void testQuadratic2x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(3, 0);
@@ -2368,7 +2368,7 @@
 
 static void testQuadratic3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 1, 0);
     path.lineTo(0, 2);
@@ -2395,7 +2395,7 @@
 
 static void testQuadratic4x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 1, 0);
     path.lineTo(0, 2);
@@ -2501,7 +2501,7 @@
 
 static void testQuadratic17x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 3, 1);
     path.lineTo(0, 2);
@@ -2892,7 +2892,7 @@
 
 static void testQuadratic59x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(2, 2);
@@ -2906,7 +2906,7 @@
 
 static void testQuadratic59(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 0, 0);
     path.lineTo(2, 2);
@@ -2959,7 +2959,7 @@
 
 static void testQuadratic67x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 2, 1);
     path.lineTo(2, 2);
@@ -3000,7 +3000,7 @@
 
 static void testQuadratic70x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(1, 0, 0, 1);
     path.lineTo(1, 2);
@@ -3348,7 +3348,7 @@
 
 static void testQuadratic89x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(3, 1, 2, 2);
     path.lineTo(0, 3);
@@ -3362,7 +3362,7 @@
 
 static void testQuadratic90x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(3, 0, 2, 2);
     path.lineTo(1, 3);
@@ -3389,7 +3389,7 @@
 
 static void testQuadratic92x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.quadTo(3, 0, 2, 2);
     path.lineTo(2, 2);
@@ -3403,109 +3403,109 @@
 
 static void testLine82(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(20, 0, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(24, 32, 33, 36, SkPath::kCCW_Direction);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(24, 32, 33, 36, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82c(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82d(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82e(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82f(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82g(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine82h(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 6, 10, SkPath::kCCW_Direction);
-    path.addRect(2, 2, 4, 4, SkPath::kCCW_Direction);
-    path.addRect(2, 6, 4, 8, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 6, 10, SkPathDirection::kCCW);
+    path.addRect(2, 2, 4, 4, SkPathDirection::kCCW);
+    path.addRect(2, 6, 4, 8, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine83(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.addRect(10, 30, 30, 40, SkPath::kCCW_Direction);
-path.addRect(0, 12, 12, 18, SkPath::kCCW_Direction);
-path.addRect(4, 13, 13, 16, SkPath::kCCW_Direction);
+path.addRect(10, 30, 30, 40, SkPathDirection::kCCW);
+path.addRect(0, 12, 12, 18, SkPathDirection::kCCW);
+path.addRect(4, 13, 13, 16, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine84(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 12, 60, 30, SkPath::kCCW_Direction);
-    path.addRect(10, 20, 40, 30, SkPath::kCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCW_Direction);
+    path.addRect(0, 12, 60, 30, SkPathDirection::kCCW);
+    path.addRect(10, 20, 40, 30, SkPathDirection::kCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine84x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 12, 60, 30, SkPath::kCCW_Direction);
-    path.addRect(10, 20, 40, 30, SkPath::kCCW_Direction);
-    path.addRect(0, 12, 12, 12, SkPath::kCCW_Direction);
-    path.addRect(4, 12, 13, 13, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 12, 60, 30, SkPathDirection::kCCW);
+    path.addRect(10, 20, 40, 30, SkPathDirection::kCCW);
+    path.addRect(0, 12, 12, 12, SkPathDirection::kCCW);
+    path.addRect(4, 12, 13, 13, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testLine85(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(36, 0, 66, 60, SkPath::kCCW_Direction);
-    path.addRect(20, 0, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(12, 0, 24, 24, SkPath::kCCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(36, 0, 66, 60, SkPathDirection::kCCW);
+    path.addRect(20, 0, 40, 40, SkPathDirection::kCCW);
+    path.addRect(12, 0, 24, 24, SkPathDirection::kCCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
@@ -3814,7 +3814,7 @@
 
 static void skphealth_com76s(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(708.099182f, 7.09919119f);
     path.lineTo(708.099182f, 7.09920025f);
     path.quadTo(704.000000f, 11.2010098f, 704.000000f, 17.0000000f);
@@ -3847,16 +3847,16 @@
 
 static void testRect1s(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
-    path.addRect(30, 20, 50, 50, SkPath::kCCW_Direction);
-    path.addRect(24, 20, 36, 30, SkPath::kCCW_Direction);
-    path.addRect(32, 24, 36, 41, SkPath::kCCW_Direction);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCCW);
+    path.addRect(30, 20, 50, 50, SkPathDirection::kCCW);
+    path.addRect(24, 20, 36, 30, SkPathDirection::kCCW);
+    path.addRect(32, 24, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testRect2s(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 0);
     path.lineTo(60, 0);
     path.lineTo(60, 60);
@@ -3882,7 +3882,7 @@
 
 static void testTriangles3x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.quadTo(0, 1, 3, 2);
     path.lineTo(1, 3);
@@ -3909,7 +3909,7 @@
 
 static void testTriangles4x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(2, 0, 0, 3);
     path.lineTo(2, 3);
@@ -3923,7 +3923,7 @@
 
 static void testQuad9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.quadTo(0, 1, 3, 2);
     path.lineTo(1, 3);
@@ -3976,7 +3976,7 @@
 
 static void testQuadralateral3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(0, 0);
@@ -4047,7 +4047,7 @@
 
 static void testQuad14(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 1, 1);
     path.lineTo(1, 2);
@@ -4349,7 +4349,7 @@
 
 static void testQuads37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1, 0);
     path.quadTo(2, 0, 1, 2);
     path.lineTo(2, 2);
@@ -4512,7 +4512,7 @@
 
 static void testQuads46x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(2, 0);
     path.quadTo(0, 1, 3, 2);
     path.lineTo(1, 3);
@@ -4643,7 +4643,7 @@
 
 static void testQuads61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 0);
     path.quadTo(0, 0, 2, 0);
     path.lineTo(1, 1);
@@ -4657,7 +4657,7 @@
 
 static void testQuadralateral10(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(0, 0);
     path.lineTo(0, 0);
     path.lineTo(0, 0);
@@ -4673,21 +4673,21 @@
 
 static void testRect3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
-    path.addRect(10, 30, 40, 30, SkPath::kCCW_Direction);
-    path.addRect(24, 6, 36, 36, SkPath::kCCW_Direction);
-    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 60, 60, SkPathDirection::kCCW);
+    path.addRect(10, 30, 40, 30, SkPathDirection::kCCW);
+    path.addRect(24, 6, 36, 36, SkPathDirection::kCCW);
+    path.addRect(32, 6, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
 static void testRect4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
-    path.addRect(0, 0, 30, 60, SkPath::kCCW_Direction);
-    path.addRect(10, 0, 40, 30, SkPath::kCCW_Direction);
-    path.addRect(20, 0, 30, 40, SkPath::kCCW_Direction);
-    path.addRect(32, 0, 36, 41, SkPath::kCCW_Direction);
+    path.setFillType(SkPathFillType::kEvenOdd);
+    path.addRect(0, 0, 30, 60, SkPathDirection::kCCW);
+    path.addRect(10, 0, 40, 30, SkPathDirection::kCCW);
+    path.addRect(20, 0, 30, 40, SkPathDirection::kCCW);
+    path.addRect(32, 0, 36, 41, SkPathDirection::kCCW);
     testSimplify(reporter, path, filename);
 }
 
@@ -4897,7 +4897,7 @@
 
 static void fuzz994s_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
 path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x42b40000));  // 10, 90
 path.lineTo(SkBits2Float(0x41200000), SkBits2Float(0x41f00000));  // 10, 30
@@ -4963,7 +4963,7 @@
 
 static void fuzz994s_3414(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42c80000), SkBits2Float(0x42480000));  // 100, 50
 path.conicTo(SkBits2Float(0x42c80000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x00000000), SkBits2Float(0x3f3504f3));  // 100, 0, 50, 0, 0.707107f
 path.conicTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x00000000), SkBits2Float(0x42480000), SkBits2Float(0x3f3504f3));  // 0, 0, 0, 50, 0.707107f
@@ -5000,7 +5000,7 @@
 
 static void fuzz_twister(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(0, 600);
 path.lineTo(3.35544e+07f, 600);
 path.lineTo(3.35544e+07f, 0);
@@ -5061,7 +5061,7 @@
 
 static void fuzz763_4713_b(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42240000), SkBits2Float(0x42040000));
 path.quadTo(SkBits2Float(0x42240000), SkBits2Float(0x4211413d), SkBits2Float(0x421aa09e), SkBits2Float(0x421aa09e));
 path.quadTo(SkBits2Float(0x4211413d), SkBits2Float(0x42240000), SkBits2Float(0x42040000), SkBits2Float(0x42240000));
@@ -5553,7 +5553,7 @@
 
 static void tiger8_393(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666));  // 92.6f, 427.3f
 path.cubicTo(SkBits2Float(0x42b93333), SkBits2Float(0x43d5a666), SkBits2Float(0x42b5cccd), SkBits2Float(0x43da1999), SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333));  // 92.6f, 427.3f, 90.9f, 436.2f, 92, 443.9f
 path.cubicTo(SkBits2Float(0x42b80000), SkBits2Float(0x43ddf333), SkBits2Float(0x42b30000), SkBits2Float(0x43e17333), SkBits2Float(0x42cf999a), SkBits2Float(0x43e1b333));  // 92, 443.9f, 89.5f, 450.9f, 103.8f, 451.4f
@@ -5565,7 +5565,7 @@
 // triggers angle assert from distance field code
 static void carsvg_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4393d61e), SkBits2Float(0x43e768f9));  // 295.673f, 462.82f
 path.cubicTo(SkBits2Float(0x4396b50e), SkBits2Float(0x43e63c20), SkBits2Float(0x43998931), SkBits2Float(0x43e6c43e), SkBits2Float(0x439cb6a8), SkBits2Float(0x43e70ef9));  // 301.414f, 460.47f, 307.072f, 461.533f, 313.427f, 462.117f
 path.cubicTo(SkBits2Float(0x439dfc1e), SkBits2Float(0x43e72ce0), SkBits2Float(0x439a285c), SkBits2Float(0x43e717fb), SkBits2Float(0x4398e23c), SkBits2Float(0x43e7027c));  // 315.97f, 462.351f, 308.315f, 462.187f, 305.767f, 462.019f
@@ -5584,7 +5584,7 @@
 
 static void simplifyTest_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x42bfefd4), SkBits2Float(0x42ef80ef));  // 95.9684f, 119.752f
 path.quadTo(SkBits2Float(0x42c26810), SkBits2Float(0x42e214b8), SkBits2Float(0x42cdcad5), SkBits2Float(0x42d82aa2));  // 97.2032f, 113.04f, 102.896f, 108.083f
 path.lineTo(SkBits2Float(0x42cdcb21), SkBits2Float(0x42d82a61));  // 102.897f, 108.083f
@@ -5629,7 +5629,7 @@
 
 static void joel_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(144.859f, 285.172f);
 path.lineTo(144.859f, 285.172f);
 path.lineTo(144.859f, 285.172f);
@@ -5686,7 +5686,7 @@
 
 static void joel_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 
 path.moveTo(403.283f, 497.197f);
 path.cubicTo(403.424f, 497.244f, 391.111f, 495.556f, 391.111f, 495.556f);
@@ -5721,7 +5721,7 @@
 
 static void joel_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(391.097f, 334.453f);
 path.lineTo(390.761f, 334.617f);
 path.lineTo(390.425f, 333.937f);
@@ -5784,7 +5784,7 @@
 
 static void joel_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4199d4fe), SkBits2Float(0x4265ac08));  // 19.229f, 57.418f
 path.cubicTo(SkBits2Float(0x419be979), SkBits2Float(0x426574bc), SkBits2Float(0x419c2b02), SkBits2Float(0x42653c6a), SkBits2Float(0x419af5c3), SkBits2Float(0x42645f3b));  // 19.489f, 57.364f, 19.521f, 57.309f, 19.37f, 57.093f
 path.cubicTo(SkBits2Float(0x419a1894), SkBits2Float(0x4263a3d7), SkBits2Float(0x4198cccd), SkBits2Float(0x4262f2b0), SkBits2Float(0x4197c290), SkBits2Float(0x4262374b));  // 19.262f, 56.91f, 19.1f, 56.737f, 18.97f, 56.554f
@@ -6538,7 +6538,7 @@
 
 static void joel_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f
 path.lineTo(SkBits2Float(0x43c5145a), SkBits2Float(0x43dc82f2));  // 394.159f, 441.023f
 path.close();
@@ -7163,7 +7163,7 @@
 
 static void joel_12x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     make_joel_12(path);
     testSimplify(reporter, path, filename);
 }
@@ -7186,7 +7186,7 @@
 
 static void joel_13x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     make_joel_13(path);
     testSimplify(reporter, path, filename);
 }
@@ -7384,7 +7384,7 @@
 
 static void joel_14x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     make_joel_14(path);
 testSimplify(reporter, path, filename);
 }
@@ -7540,7 +7540,7 @@
 
 static void joel_15x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     make_joel_15(path);
 testSimplify(reporter, path, filename);
 }
@@ -7678,7 +7678,7 @@
 
 static void joel_16x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     make_joel_16(path);
 testSimplify(reporter, path, filename);
 }
@@ -7698,7 +7698,7 @@
 
 static void grshapearc(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(25.0098f, 23.1973f);
 path.lineTo(25.5689f, 22.3682f);
 path.conicTo(26.1281f, 21.5392f, 26.9572f, 22.0984f, 0.707107f);
@@ -9273,7 +9273,7 @@
 
 static void bug8249(skiatest::Reporter* reporter, const char* filename) {
 SkPath path;
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x43310000), SkBits2Float(0x43810000));  // 177, 258
 path.lineTo(SkBits2Float(0x43480000), SkBits2Float(0x43868000));  // 200, 269
 path.cubicTo(SkBits2Float(0x43480000), SkBits2Float(0x43b20000), SkBits2Float(0x437a0000), SkBits2Float(0x43cd0000), SkBits2Float(0x43c80000), SkBits2Float(0x43cd0000));  // 200, 356, 250, 410, 400, 410
@@ -9300,7 +9300,7 @@
 
 static void bug8290(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(-1e+09, -1e+09);
     path.lineTo(1e+09, -1e+09);
     path.lineTo(1e+09, 1e+09);
diff --git a/tests/PathOpsSimplifyTrianglesThreadedTest.cpp b/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
index 52f0a84..d9bdb53 100644
--- a/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
+++ b/tests/PathOpsSimplifyTrianglesThreadedTest.cpp
@@ -35,7 +35,6 @@
                 }
                 SkString pathStr;
                 SkPath path, out;
-                path.setFillType(SkPath::kWinding_FillType);
                 path.moveTo(SkIntToScalar(ax), SkIntToScalar(ay));
                 path.lineTo(SkIntToScalar(bx), SkIntToScalar(by));
                 path.lineTo(SkIntToScalar(cx), SkIntToScalar(cy));
@@ -53,13 +52,13 @@
                     pathStr.appendf("    path.lineTo(%d, %d);\n", ex, ey);
                     pathStr.appendf("    path.lineTo(%d, %d);\n", fx, fy);
                     pathStr.appendf("    path.close();\n");
-                    state.outputProgress(pathStr.c_str(), SkPath::kWinding_FillType);
+                    state.outputProgress(pathStr.c_str(), SkPathFillType::kWinding);
                 }
                 ShowTestName(&state, d, e, f, 0);
                 testSimplify(path, false, out, state, pathStr.c_str());
-                path.setFillType(SkPath::kEvenOdd_FillType);
+                path.setFillType(SkPathFillType::kEvenOdd);
                 if (state.fReporter->verbose()) {
-                    state.outputProgress(pathStr.c_str(), SkPath::kEvenOdd_FillType);
+                    state.outputProgress(pathStr.c_str(), SkPathFillType::kEvenOdd);
                 }
                 ShowTestName(&state, d, e, f, 1);
                 testSimplify(path, true, out, state, pathStr.c_str());
diff --git a/tests/PathOpsSkpTest.cpp b/tests/PathOpsSkpTest.cpp
index efbe0e5..dd0c9ac 100644
--- a/tests/PathOpsSkpTest.cpp
+++ b/tests/PathOpsSkpTest.cpp
@@ -10,7 +10,7 @@
 
 static void skpcheeseandburger_com225(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(555, 468);
     path.lineTo(555, 362);
     path.lineTo(872, 362);
@@ -18,7 +18,7 @@
     path.lineTo(555, 468);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(859.11792f, 397.320343f);
     pathB.cubicTo(855.523071f, 399.691284f, 853.721191f, 402.40863f, 853.721191f, 405.552216f);
     pathB.cubicTo(853.721191f, 407.911163f, 854.727478f, 410.115387f, 857.043518f, 412.252716f);
@@ -371,7 +371,7 @@
 
 static void skpeverytechpro_blogspot_com100(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1074.29285f, 627.292786f);
     path.quadTo(1074.58582f, 627, 1075, 627);
     path.lineTo(1117, 627);
@@ -395,7 +395,7 @@
     path.lineTo(1076, 629);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1074, 627);
     pathB.lineTo(1075, 628);
     pathB.lineTo(1116.5f, 644.5f);
@@ -405,7 +405,7 @@
 
 static void skpflite_com41(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(301.464081f, 424);
     path.lineTo(296, 433.46405f);
     path.lineTo(296, 433.810822f);
@@ -417,7 +417,7 @@
     path.lineTo(301.464081f, 424);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(302.849854f, 421.599762f);
     pathB.lineTo(311.510101f, 426.599762f);
     pathB.lineTo(304.510101f, 438.724121f);
@@ -428,7 +428,7 @@
 
 static void skpilkoora_com37(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(818, 157);
     path.cubicTo(818, 148.715729f, 824.715698f, 142, 833, 142);
     path.lineTo(909, 142);
@@ -450,7 +450,7 @@
     path.lineTo(1184, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1185, 142);
     pathB.lineTo(1001.5f, 325.5f);
     pathB.lineTo(1001.5f, 782.5f);
@@ -460,7 +460,7 @@
 
 static void skpmm4everfriends_com43(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(540.74231f, 215.922546f);
     path.cubicTo(540.893127f, 215.391159f, 541.443909f, 215.090134f, 541.972473f, 215.250168f);
     path.lineTo(581.213318f, 227.131104f);
@@ -472,7 +472,7 @@
     path.lineTo(540.74231f, 215.922546f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(541.015381f, 214.960388f);
     pathB.lineTo(582.17041f, 227.420883f);
     pathB.lineTo(576.435852f, 247.626068f);
@@ -483,7 +483,7 @@
 
 static void skpmtrk_uz27(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(33, 787);
     path.lineTo(33, 412);
     path.lineTo(1233, 412);
@@ -495,7 +495,7 @@
     path.quadTo(33, 793.213196f, 33, 787);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(33, 412);
     pathB.lineTo(1233, 412);
     pathB.lineTo(1233, 787);
@@ -510,7 +510,7 @@
 
 static void skpfrauen_magazin_com83(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(808, 886);
     path.cubicTo(805.581055f, 886, 803.563293f, 887.717773f, 803.100037f, 890);
     path.lineTo(1122.90002f, 890);
@@ -518,7 +518,7 @@
     path.lineTo(808, 886);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(808, 886);
     pathB.lineTo(1118, 886);
     pathB.cubicTo(1120.76147f, 886, 1123, 888.238586f, 1123, 891);
@@ -534,7 +534,7 @@
 
 static void skpi_gino_com16(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(184, 734);
     path.quadTo(133.051727f, 734, 97.0258636f, 770.025879f);
     path.quadTo(61, 806.051758f, 61, 857);
@@ -545,7 +545,7 @@
     path.quadTo(234.948273f, 734, 184, 734);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(185, 734);
     pathB.cubicTo(252.93103f, 734, 308, 789.06897f, 308, 857);
     pathB.cubicTo(308, 924.93103f, 252.93103f, 980, 185, 980);
@@ -559,7 +559,7 @@
 
 static void skppchappy_com_au102(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(363, 493);
     path.cubicTo(360.790863f, 493, 359, 494.790863f, 359, 497);
     path.lineTo(359, 656);
@@ -571,7 +571,7 @@
     path.lineTo(363, 493);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(362, 494);
     pathB.lineTo(623, 494);
     pathB.cubicTo(624.65686f, 494, 626, 494.895416f, 626, 496);
@@ -587,7 +587,7 @@
 
 static void skpsciality_com161(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(656, 728);
     path.cubicTo(653.790833f, 728, 652, 729.790833f, 652, 732);
     path.lineTo(652, 789);
@@ -599,7 +599,7 @@
     path.lineTo(656, 728);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(655, 729);
     pathB.lineTo(769, 729);
     pathB.cubicTo(770.65686f, 729, 772, 729.895447f, 772, 731);
@@ -615,7 +615,7 @@
 
 static void skpsudoestenegocios_com186(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 495);
     path.lineTo(1.23685242e-14f, 293);
     path.lineTo(44, 293);
@@ -635,7 +635,7 @@
     path.lineTo(1, 294);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(48, 495);
     pathB.lineTo(24, 471);
     pathB.lineTo(24, 317);
@@ -645,7 +645,7 @@
 
 static void skpthesuburbanite_com213(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(863.439026f, 692);
     path.lineTo(863.283264f, 692);
     path.lineTo(802, 708.420837f);
@@ -655,7 +655,7 @@
     path.lineTo(863.439026f, 692);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(783.256775f, 713.443054f);
     pathB.lineTo(863.428589f, 691.96106f);
     pathB.lineTo(866.016724f, 701.620361f);
@@ -666,7 +666,7 @@
 
 static void skphostloco_com11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(6.66133815e-16f, 648);
     path.lineTo(25.8522835f, 648);
     path.quadTo(27.5087376f, 647.999634f, 28.6807098f, 646.82843f);
@@ -678,7 +678,7 @@
     path.lineTo(6.66133815e-16f, 648);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(0, 463);
     pathB.lineTo(30, 463);
     pathB.lineTo(30, 648);
@@ -689,7 +689,7 @@
 
 static void skpsergeychunkevich_com8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 926);
     path.lineTo(0, 0);
     path.lineTo(1265, 0);
@@ -697,7 +697,7 @@
     path.lineTo(0, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(37, 374);
     pathB.lineTo(37, 535);
     pathB.cubicTo(37, 536.65686f, 35.6568565f, 538, 34, 538);
@@ -711,7 +711,7 @@
 
 static void skptracksflow_com9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(16, 56);
     path.lineTo(32, 56);
     path.lineTo(32, 72);
@@ -719,7 +719,7 @@
     path.lineTo(16, 56);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(31.65625f, 70.0555649f);
     pathB.lineTo(31.65625f, 70.0554962f);
     pathB.lineTo(26.9727192f, 65.3615341f);
@@ -745,7 +745,7 @@
 
 static void skpautobutler_dk29(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 926);
     path.lineTo(0, 0);
     path.lineTo(1265, 0);
@@ -753,7 +753,7 @@
     path.lineTo(0, 926);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(21, 162);
     pathB.lineTo(21, 301);
     pathB.lineTo(8.57224448e-15f, 301);
@@ -764,7 +764,7 @@
 
 static void skponlinecollege_org144(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(179, 407);
     path.cubicTo(177.34314f, 407, 176, 408.34314f, 176, 410);
     path.lineTo(176, 436);
@@ -776,7 +776,7 @@
     path.lineTo(179, 407);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(179, 408);
     pathB.lineTo(337, 408);
     pathB.cubicTo(338.65686f, 408, 340, 408.895416f, 340, 410);
@@ -792,7 +792,7 @@
 
 static void skpnational_com_au81(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(807, 817);
     path.quadTo(806.585876f, 817.000122f, 806.292908f, 817.292908f);
     path.quadTo(806.000122f, 817.585876f, 806, 818);
@@ -804,7 +804,7 @@
     path.lineTo(807, 817);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(807, 817);
     pathB.lineTo(1110, 817);
     pathB.cubicTo(1110.55225f, 817, 1111, 817.447693f, 1111, 818);
@@ -818,7 +818,7 @@
 
 static void skprentacheat_com30(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(967, 263);
     path.quadTo(966.585876f, 263.000092f, 966.292908f, 263.292908f);
     path.quadTo(966.000122f, 263.585876f, 966, 264);
@@ -830,7 +830,7 @@
     path.lineTo(967, 263);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(967, 263);
     pathB.lineTo(1213, 263);
     pathB.cubicTo(1213.55225f, 263, 1214, 263.447723f, 1214, 264);
@@ -844,7 +844,7 @@
 
 static void skpbreakmystyle_com10(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(271.032867f, -5.32907052e-15f);
     path.lineTo(56.9671326f, -5.16253706e-15f);
     path.quadTo(52.7835083f, 3.69968891f, 48.7416f, 7.74160004f);
@@ -857,7 +857,7 @@
     path.quadTo(275.216431f, 3.69964004f, 271.032867f, -5.32907052e-15f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(327, 123);
     pathB.quadTo(327, 190.516815f, 279.258392f, 238.258392f);
     pathB.quadTo(231.516815f, 286, 164, 286);
@@ -873,7 +873,7 @@
 
 static void skpsd_graphic_net104(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(475.421448f, 836.985962f);
     path.lineTo(461.280975f, 841.990662f);
     path.cubicTo(466.80899f, 857.609802f, 458.62854f, 874.752991f, 443.009399f, 880.281006f);
@@ -888,7 +888,7 @@
     path.quadTo(477.528076f, 842.93811f, 475.421448f, 836.985962f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(405.592621f, 909.435547f);
     pathB.lineTo(390.578583f, 867.014099f);
     pathB.lineTo(433, 852.000061f);
@@ -898,7 +898,7 @@
 
 static void skpnaoxrane_ru23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(458.703552f, 275.050262f);
     path.quadTo(487.41687f, 273.000702f, 528, 273);
     path.lineTo(529, 273);
@@ -914,7 +914,7 @@
     path.quadTo(430.009796f, 277.101196f, 458.703552f, 275.050262f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(528, 278);
     pathB.lineTo(529, 278);
     pathB.cubicTo(530.65686f, 278, 532, 278, 532, 278);
@@ -930,7 +930,7 @@
 
 static void skptcmevents_org23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(465.503998f, 546);
     path.lineTo(347, 546);
     path.lineTo(347, 632);
@@ -942,7 +942,7 @@
     path.quadTo(465.670349f, 546.601501f, 465.503998f, 546);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(363.052246f, 542.495361f);
     pathB.lineTo(463.779907f, 542.671143f);
     pathB.cubicTo(464.829529f, 542.672974f, 466.946289f, 550.755676f, 468.507751f, 560.724426f);
@@ -958,7 +958,7 @@
 
 static void skpredbullskatearcade_es16(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(936.765625f, 458.965302f);
     path.cubicTo(937.028442f, 453.863251f, 933.145813f, 449.864502f, 928.093445f, 450.033905f);
     path.lineTo(661.882263f, 458.958862f);
@@ -973,7 +973,7 @@
     path.lineTo(936.765625f, 458.965302f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(661.882263f, 458.958862f);
     pathB.lineTo(928.093445f, 450.033905f);
     pathB.cubicTo(929.103882f, 450, 929.709961f, 454.108612f, 929.447144f, 459.210663f);
@@ -989,7 +989,7 @@
 
 static void skpfinanzasdigital_com9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(156, 126);
     path.quadTo(154.343552f, 126.000397f, 153.17157f, 127.17157f);
     path.quadTo(152.000397f, 128.343552f, 152, 130);
@@ -1001,7 +1001,7 @@
     path.lineTo(156, 126);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(156, 126);
     pathB.lineTo(1110, 126);
     pathB.cubicTo(1111.65686f, 126, 1113, 127.790863f, 1113, 130);
@@ -1015,7 +1015,7 @@
 
 static void skppartainasdemo250_org56(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(182.000015f, 645);
     path.lineTo(182, 640);
     path.cubicTo(174.322327f, 640, 166.644669f, 637.071045f, 160.786804f, 631.213196f);
@@ -1028,7 +1028,7 @@
     path.quadTo(167.517334f, 645, 182.000015f, 645);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(182, 659.497498f);
     pathB.lineTo(206.748749f, 634.748718f);
     pathB.lineTo(182.000015f, 610);
@@ -1038,7 +1038,7 @@
 
 static void skpmlk_com326(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(154, 670);
     path.cubicTo(151.238571f, 670, 149, 672.238586f, 149, 675);
     path.lineTo(149, 710.001465f);
@@ -1050,7 +1050,7 @@
     path.lineTo(154, 670);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(154, 671);
     pathB.lineTo(188, 671);
     pathB.cubicTo(190.761429f, 671, 193, 672.790833f, 193, 675);
@@ -1066,7 +1066,7 @@
 
 static void skpcyclist_friends_gr52(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(50, 182);
     path.lineTo(1215, 182);
     path.lineTo(1215, 202);
@@ -1078,7 +1078,7 @@
     path.lineTo(50, 182);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(50, 183);
     pathB.lineTo(1215, 183);
     pathB.lineTo(1215, 202);
@@ -1092,7 +1092,7 @@
 
 static void skpwww_fj_p_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(172, 201);
     path.lineTo(172, 202);
     path.lineTo(220, 202);
@@ -1101,7 +1101,7 @@
     path.lineTo(172, 201);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(161, 202);
     pathB.lineTo(161, 199);
     pathB.lineTo(223, 199.000015f);
@@ -1111,7 +1111,7 @@
 
 static void skpwww_lavoixdunord_fr_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(806, 57);
     path.cubicTo(806, 55.3431473f, 807.34314f, 54, 809, 54);
     path.lineTo(930, 54);
@@ -1133,7 +1133,7 @@
     path.lineTo(808, 58);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(806, 54);
     pathB.lineTo(808, 56);
     pathB.lineTo(935.02002f, 56.0200005f);
@@ -1143,7 +1143,7 @@
 
 static void skppptv_com_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(173, 5342);
     path.quadTo(171.343536f, 5342.00049f, 170.17157f, 5343.17139f);
     path.quadTo(169.000397f, 5344.34375f, 169, 5346);
@@ -1155,7 +1155,7 @@
     path.lineTo(173, 5342);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(173, 5342);
     pathB.lineTo(230, 5342);
     pathB.cubicTo(231.65686f, 5342, 233, 5343.79102f, 233, 5346);
@@ -1169,7 +1169,7 @@
 
 static void skpwww_booking_com_68(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(90, 187);
     path.cubicTo(90, 185.34314f, 91.3431473f, 184, 93, 184);
     path.lineTo(588, 184);
@@ -1191,7 +1191,7 @@
     path.lineTo(92, 188);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(90, 184);
     pathB.lineTo(92, 186);
     pathB.lineTo(593.02002f, 186.020004f);
@@ -1201,7 +1201,7 @@
 
 static void skpwww_despegar_com_mx_272(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(635, 1788);
     path.cubicTo(635, 1786.34314f, 636.34314f, 1785, 638, 1785);
     path.lineTo(832, 1785);
@@ -1223,7 +1223,7 @@
     path.lineTo(637, 1789);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(835, 1785);
     pathB.lineTo(833, 1787);
     pathB.lineTo(832.97998f, 1817.02002f);
@@ -1233,7 +1233,7 @@
 
 static void skpwww_joomla_org_23(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(320, 347);
     path.cubicTo(320, 344.238586f, 322.238586f, 342, 325, 342);
     path.lineTo(416, 342);
@@ -1251,7 +1251,7 @@
     path.cubicTo(322.238586f, 382, 320, 380.209137f, 320, 378);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(320, 383);
     pathB.lineTo(320, 378);
     pathB.lineTo(421, 378.000031f);
@@ -1261,7 +1261,7 @@
 
 static void skpwww_macrumors_com_131(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(136, 14089);
     path.lineTo(136, 14056);
     path.lineTo(778, 14056);
@@ -1273,7 +1273,7 @@
     path.quadTo(136.000397f, 14090.6562f, 136, 14089);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(136, 14057);
     pathB.lineTo(778, 14057);
     pathB.lineTo(778, 14089);
@@ -1287,7 +1287,7 @@
 
 static void skpwww_leadpages_net_84(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(377.1716f, 5910.17139f);
     path.cubicTo(376.447723f, 5910.89551f, 376, 5911.89551f, 376, 5913);
     path.lineTo(376, 5972);
@@ -1298,7 +1298,7 @@
     path.lineTo(377.1716f, 5910.17139f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(376, 5909);
     pathB.lineTo(378.481873f, 5909);
     pathB.lineTo(379.999878f, 5976);
@@ -1308,7 +1308,7 @@
 
 static void skpwww_briian_com_34(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(843, 216);
     path.cubicTo(843, 213.238571f, 845.238586f, 211, 848, 211);
     path.lineTo(1191, 211);
@@ -1330,7 +1330,7 @@
     path.lineTo(844, 217);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(843, 784);
     pathB.lineTo(843, 779);
     pathB.lineTo(1196, 779.000061f);
@@ -1340,7 +1340,7 @@
 
 static void skpwww_sciality_com_100(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(162, 468);
     path.cubicTo(159.790863f, 468, 158, 469.790863f, 158, 472);
     path.lineTo(158, 528);
@@ -1352,7 +1352,7 @@
     path.lineTo(162, 468);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(275, 468);
     pathB.cubicTo(276.65686f, 468, 278, 469.34314f, 278, 471);
     pathB.lineTo(278, 529);
@@ -1368,7 +1368,7 @@
 
 static void skpwww_sciality_com_101(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(162, 468);
     path.cubicTo(159.790863f, 468, 158, 469.790863f, 158, 472);
     path.lineTo(158, 528);
@@ -1380,7 +1380,7 @@
     path.lineTo(162, 468);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(161, 469);
     pathB.lineTo(275, 469);
     pathB.cubicTo(276.65686f, 469, 278, 469.895416f, 278, 471);
@@ -1396,7 +1396,7 @@
 
 static void skpwww_meb_gov_tr_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(137.34314f, 145.34314f);
     path.quadTo(139.687088f, 143.000793f, 143, 143);
     path.lineTo(242, 143);
@@ -1408,7 +1408,7 @@
     path.quadTo(135.000793f, 147.687088f, 137.34314f, 145.34314f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(135, 143);
     pathB.lineTo(250, 143);
     pathB.lineTo(250, 177);
@@ -1419,7 +1419,7 @@
 
 static void skpwww_meb_gov_tr_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(143, 143);
     path.quadTo(139.687088f, 143.000793f, 137.34314f, 145.34314f);
     path.quadTo(135.000793f, 147.687088f, 135, 151);
@@ -1431,7 +1431,7 @@
     path.lineTo(143, 143);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(143, 143);
     pathB.lineTo(242, 143);
     pathB.cubicTo(245.865997f, 143, 249, 146.581726f, 249, 151);
@@ -1445,7 +1445,7 @@
 
 static void skpgithub_io_25(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1001.87866f, 14.8786793f);
     path.quadTo(1002.75745f, 14.0001001f, 1004, 14);
     path.lineTo(1105, 14);
@@ -1461,7 +1461,7 @@
     path.quadTo(1001.00012f, 15.7574596f, 1001.87866f, 14.8786793f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(1005, 16);
     pathB.lineTo(1104, 16);
     pathB.cubicTo(1105.10461f, 16, 1106, 16.8954296f, 1106, 18);
@@ -1477,7 +1477,7 @@
 
 static void skpgithub_io_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1001.87866f, 14.8786793f);
     path.quadTo(1002.75745f, 14.0001001f, 1004, 14);
     path.lineTo(1105, 14);
@@ -1503,7 +1503,7 @@
     path.lineTo(1003, 18);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1108, 14);
     pathB.lineTo(1106, 16);
     pathB.lineTo(1105.97998f, 46.0200005f);
@@ -1513,7 +1513,7 @@
 
 static void skpskpicture14(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 994);
     path.lineTo(0, 0);
     path.lineTo(512, 0);
@@ -1521,7 +1521,7 @@
     path.lineTo(0, 994);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(-317, 168);
     pathB.quadTo(-317, 166.757385f, -316.121338f, 165.878662f);
     pathB.quadTo(-315.242645f, 165, -314, 165);
@@ -1536,7 +1536,7 @@
 
 static void skpskpicture15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 193);
     path.lineTo(323, 193);
     path.lineTo(323, 168);
@@ -1546,7 +1546,7 @@
     path.lineTo(0, 193);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.setFillType(SkPathFillType::kInverseWinding);
     pathB.moveTo(-314, 165);
     pathB.lineTo(320, 165);
     pathB.cubicTo(321.65686f, 165, 323, 166.34314f, 323, 168);
@@ -1560,7 +1560,7 @@
 
 static void skpelpais_com_18(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(183, 8507);
     path.lineTo(552, 8506.99023f);
     path.lineTo(552, 8508);
@@ -1568,7 +1568,7 @@
     path.lineTo(183, 8507);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(183, 8508);
     pathB.lineTo(183, 8506.99023f);
     pathB.lineTo(552, 8507);
@@ -1578,7 +1578,7 @@
 
 static void skpnamecheap_com_405(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(140, 1000);
     path.lineTo(140, 842);
     path.lineTo(141, 842);
@@ -1586,7 +1586,7 @@
     path.lineTo(140, 1000);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(140, 842);
     pathB.lineTo(141.008835f, 837.9646f);
     pathB.lineTo(141.235291f, 1109.05884f);
@@ -1596,7 +1596,7 @@
 
 static void skpwww_alrakoba_net_62(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(134.34314f, 9802.34277f);
     path.quadTo(132, 9804.68652f, 132, 9808);
     path.lineTo(132, 9822);
@@ -1611,7 +1611,7 @@
     path.lineTo(134.34314f, 9802.34277f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(132, 9800);
     pathB.lineTo(135.962357f, 9800);
     pathB.lineTo(140, 9830);
@@ -1621,7 +1621,7 @@
 
 static void skpwww_cityads_ru_249(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(817.464478f, 11.4644661f);
     path.quadTo(818.928955f, 10, 821, 10);
     path.lineTo(998, 10);
@@ -1647,7 +1647,7 @@
     path.quadTo(816, 12.9289322f, 817.464478f, 11.4644661f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1003, 10);
     pathB.lineTo(1000, 13);
     pathB.lineTo(999.969971f, 37.0299988f);
@@ -1657,7 +1657,7 @@
 
 static void skpwww_dealnews_com_315(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(966.464478f, 4261.46436f);
     path.quadTo(965, 4262.92871f, 965, 4265);
     path.lineTo(965, 4276);
@@ -1673,7 +1673,7 @@
     path.lineTo(966.464478f, 4261.46436f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(965, 4260);
     pathB.lineTo(967.716675f, 4260);
     pathB.lineTo(970, 4281);
@@ -1683,7 +1683,7 @@
 
 static void skpwww_inmotionhosting_com_9(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(991.633911f, 1839);
     path.lineTo(964.265015f, 1839);
     path.lineTo(963.734985f, 1893.73242f);
@@ -1694,7 +1694,7 @@
     path.lineTo(991.633911f, 1839);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(964.267578f, 1838.73499f);
     pathB.lineTo(1019.26501f, 1839.26758f);
     pathB.lineTo(1018.73242f, 1894.26501f);
@@ -1705,7 +1705,7 @@
 
 static void skpwww_alucinados_net_101(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1129.53552f, 1164.46448f);
     path.lineTo(1128, 1166);
     path.lineTo(1128.12231f, 1166.49548f);
@@ -1717,7 +1717,7 @@
     path.quadTo(1131, 1165.92896f, 1129.53552f, 1164.46448f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1131, 1163);
     pathB.lineTo(-43515.8555f, -177415.594f);
     pathB.lineTo(1129.76465f, 1173.05884f);
@@ -1727,7 +1727,7 @@
 
 static void skpwww_hairjobsearch_com_31(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(143.292892f, 0.707106769f);
     path.quadTo(143, 0.414213538f, 143, 0);
     path.lineTo(1123, 0);
@@ -1737,7 +1737,7 @@
     path.quadTo(143.585785f, 1, 143.292892f, 0.707106769f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(143, 1);
     pathB.lineTo(144, 0);
     pathB.lineTo(1122, 0);
@@ -1747,7 +1747,7 @@
 
 static void skpwww_heartiste_wordpress_com_86(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(741, 9432);
     path.lineTo(761, 9431.99023f);
     path.lineTo(761, 9433);
@@ -1755,7 +1755,7 @@
     path.lineTo(741, 9432);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(741, 9433);
     pathB.lineTo(741, 9431.99023f);
     pathB.lineTo(761, 9432);
@@ -1765,7 +1765,7 @@
 
 static void skpwww_argus_presse_fr_41(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 343);
     path.lineTo(165, 343);
     path.lineTo(165, 364.869873f);
@@ -1773,7 +1773,7 @@
     path.lineTo(1000, 343);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(165, 343.000031f);
     pathB.lineTo(1000, 343.000031f);
     pathB.lineTo(1000, 364.869904f);
@@ -1784,7 +1784,7 @@
 
 static void skpwww_320kbps_net_2231(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(838, 9125);
     path.lineTo(862, 9124.99023f);
     path.lineTo(862, 9126);
@@ -1792,7 +1792,7 @@
     path.lineTo(838, 9125);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(838, 9126);
     pathB.lineTo(838, 9124.99023f);
     pathB.lineTo(862, 9125);
@@ -1802,7 +1802,7 @@
 
 static void skpwww_exystence_net_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(143, 9075);
     path.lineTo(316, 9075);
     path.lineTo(316, 9073.99023f);
@@ -1810,7 +1810,7 @@
     path.lineTo(143, 9075);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(143, 9075);
     pathB.lineTo(143, 9073.99023f);
     pathB.lineTo(316, 9074);
@@ -1820,7 +1820,7 @@
 
 static void skpwww_trashness_com_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(541.5f, 4835.99512f);
     path.lineTo(91.5f, 4836);
     path.lineTo(91.5f, 4836.5f);
@@ -1828,7 +1828,7 @@
     path.lineTo(541.5f, 4835.99512f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(91.5f, 4836.5f);
     pathB.lineTo(91.5f, 4835.99512f);
     pathB.lineTo(541.5f, 4836);
@@ -1838,7 +1838,7 @@
 
 static void skpwww_getgold_jp_731(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(284.878693f, 10134.8789f);
     path.quadTo(284, 10135.7578f, 284, 10137);
     path.lineTo(284, 10216);
@@ -1849,7 +1849,7 @@
     path.lineTo(284.878693f, 10134.8789f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(284, 10134);
     pathB.lineTo(286.05957f, 10129.8809f);
     pathB.lineTo(285.399994f, 10216.2002f);
@@ -1859,7 +1859,7 @@
 
 static void skpwww_maturesupertube_com_21(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(3.17157292f, 11831.1719f);
     path.quadTo(4.34314585f, 11830, 6, 11830);
     path.lineTo(1259, 11830);
@@ -1889,7 +1889,7 @@
     path.quadTo(3, 11832.7578f, 3.87867975f, 11831.8789f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(2, 11830);
     pathB.lineTo(4.5f, 11832.5f);
     pathB.lineTo(1260.5f, 11832.5f);
@@ -1899,7 +1899,7 @@
 
 static void skpwww_hubbyscook_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 902.329346f);
     path.quadTo(998, 905.250427f, 998, 909);
     path.lineTo(998, 910);
@@ -1907,7 +1907,7 @@
     path.lineTo(1000, 902.329346f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(998, 910);
     pathB.lineTo(998, 909);
     pathB.quadTo(998, 904.029419f, 1001.51471f, 900.514709f);
@@ -1927,7 +1927,7 @@
 
 static void skpwww_gruposejaumdivulgador_com_br_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(610.5f, 5.78626502e-14f);
     path.lineTo(1083.5f, -6.12303177e-17f);
     path.lineTo(1083.5f, 469);
@@ -1935,7 +1935,7 @@
     path.lineTo(610.5f, 5.78626502e-14f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(611, 0);
     pathB.lineTo(1084, 0);
     pathB.lineTo(1084, 469);
@@ -1946,7 +1946,7 @@
 
 static void skpwww_phototransferapp_com_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(85.6091843f, 5.92893219f);
     path.quadTo(89.6041641f, 3, 93.7462997f, 3);
     path.lineTo(1212.74634f, 3);
@@ -1960,7 +1960,7 @@
     path.quadTo(81.614212f, 8.85786438f, 85.6091843f, 5.92893219f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(83.7462997f, 3);
     pathB.lineTo(1222.74634f, 3);
     pathB.lineTo(1219.10657f, 13);
@@ -1971,7 +1971,7 @@
 
 static void skpwww_phototransferapp_com_24x(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(85.6091843f, 5.92893219f);
     path.quadTo(89.6041641f, 3, 93.7462997f, 3);
     path.lineTo(112.74634f, 3);
@@ -1985,7 +1985,7 @@
     path.quadTo(81.614212f, 8.85786438f, 85.6091843f, 5.92893219f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(83.7462997f, 3);
     pathB.lineTo(122.74634f, 3);
     pathB.lineTo(119.10657f, 13);
@@ -1996,7 +1996,7 @@
 
 static void skpwww_helha_be_109(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(117.686981f, 3339.08423f);
     path.lineTo(109.533035f, 3350.72925f);
     path.quadTo(107.120552f, 3354.17456f, 103.879379f, 3354.41821f);
@@ -2008,7 +2008,7 @@
     path.lineTo(117.686981f, 3339.08423f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(118.269409f, 3339.91602f);
     pathB.lineTo(117.686981f, 3339.08423f);
     pathB.lineTo(98.4669647f, 3351.56104f);
@@ -2018,7 +2018,7 @@
 
 static void skpwww_cooksnaps_com_32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(509.34021f, 176);
     path.lineTo(505, 176);
     path.quadTo(500.964233f, 176, 497.299988f, 176.896912f);
@@ -2050,7 +2050,7 @@
     path.quadTo(511.503082f, 176.282898f, 509.34021f, 176);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(478.470215f, 223.683014f);
     pathB.lineTo(477.970215f, 222.816986f);
     pathB.quadTo(471.549896f, 211.696686f, 474.873322f, 199.293594f);
@@ -2070,13 +2070,13 @@
 
 static void skpwww_cooksnaps_com_32a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(497.299988f, 176.896912f);
     path.quadTo(493.678162f, 177.952286f, 490.183014f, 179.9702f);
     path.lineTo(489.316986f, 180.4702f);
     path.quadTo(485.175385f, 182.861359f, 482.115265f, 186.082397f);
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(474.873322f, 199.293594f);
     pathB.quadTo(478.196686f, 186.890503f, 489.316986f, 180.4702f);
     pathB.lineTo(490.183014f, 179.9702f);
@@ -2086,7 +2086,7 @@
 
 static void skpwww_contextualnewsfeeds_com_346(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(460.257355f, 1202.27808f);
     path.lineTo(460.257355f, 1204.27808f);
     path.quadTo(461.081207f, 1204.27808f, 461.665161f, 1203.69873f);
@@ -2099,7 +2099,7 @@
     path.lineTo(460.257355f, 1202.27808f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(460.257355f, 1205.10657f);
     pathB.lineTo(458.828979f, 1203.67822f);
     pathB.lineTo(465.914215f, 1196.62122f);
@@ -2109,7 +2109,7 @@
 
 static void skpwww_pindosiya_com_99(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(899.17157f, 548.17157f);
     path.quadTo(898, 549.34314f, 898, 551);
     path.lineTo(898, 556);
@@ -2120,7 +2120,7 @@
     path.lineTo(899.17157f, 548.17157f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(898, 547);
     pathB.lineTo(901.086914f, 547);
     pathB.lineTo(899, 556);
@@ -2130,7 +2130,7 @@
 
 static void skpwww_karnivool_com_au_11(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, 1431);
     path.lineTo(0, 775);
     path.lineTo(1265, 775);
@@ -2138,7 +2138,7 @@
     path.lineTo(0, 1431);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(32.3243904f, 851);
     pathB.lineTo(459.324402f, 851);
     pathB.lineTo(427, 1081);
@@ -2149,7 +2149,7 @@
 
 static void skpwww_tunero_de_24(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1020.79303f, 2252);
     path.quadTo(1018.72198f, 2252, 1016.86798f, 2253.46436f);
     path.quadTo(1015.02032f, 2254.92383f, 1014.4668f, 2256.98584f);
@@ -2168,7 +2168,7 @@
     path.lineTo(1020.79303f, 2252);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1011.53705f, 2268);
     pathB.lineTo(1014.46301f, 2257);
     pathB.quadTo(1015.01392f, 2254.92896f, 1016.86798f, 2253.46436f);
@@ -2188,7 +2188,7 @@
 
 static void skpwww_docgelo_com_66(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(22.5f, 24174.5f);
     path.lineTo(185.5f, 24174.498f);
     path.lineTo(185.5f, 24174.75f);
@@ -2196,7 +2196,7 @@
     path.lineTo(22.5f, 24174.5f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(22.5f, 24174.75f);
     pathB.lineTo(22.5f, 24174.498f);
     pathB.lineTo(185.5f, 24174.5f);
@@ -2206,7 +2206,7 @@
 
 static void skpwww_kpopexplorer_net_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 866.329346f);
     path.quadTo(998, 869.250427f, 998, 873);
     path.lineTo(998, 874);
@@ -2214,7 +2214,7 @@
     path.lineTo(1000, 866.329346f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(998, 874);
     pathB.lineTo(998, 873);
     pathB.quadTo(998, 868.029419f, 1001.51471f, 864.514709f);
@@ -2234,7 +2234,7 @@
 
 static void skpwww_artblart_com_8(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(22.5f, 24527.25f);
     path.lineTo(45, 24527.248f);
     path.lineTo(45, 24527.5f);
@@ -2242,7 +2242,7 @@
     path.lineTo(22.5f, 24527.25f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(22.5f, 24527.5f);
     pathB.lineTo(22.5f, 24527.248f);
     pathB.lineTo(45, 24527.25f);
@@ -2252,7 +2252,7 @@
 
 static void skpwww_jessicaslens_wordpress_com_222(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 844.329346f);
     path.quadTo(998, 847.250427f, 998, 851);
     path.lineTo(998, 852);
@@ -2260,7 +2260,7 @@
     path.lineTo(1000, 844.329346f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(998, 852);
     pathB.lineTo(998, 851);
     pathB.quadTo(998, 846.029419f, 1001.51471f, 842.514709f);
@@ -2280,7 +2280,7 @@
 
 static void skpwww_simplysaru_com_40(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 866.329346f);
     path.quadTo(998, 869.250427f, 998, 873);
     path.lineTo(998, 874);
@@ -2288,7 +2288,7 @@
     path.lineTo(1000, 866.329346f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(998, 874);
     pathB.lineTo(998, 873);
     pathB.quadTo(998, 868.029419f, 1001.51471f, 864.514709f);
@@ -2308,7 +2308,7 @@
 
 static void skpwww_partsdata_de_53(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(407, 119);
     path.lineTo(407, 28);
     path.lineTo(647, 28);
@@ -2316,7 +2316,7 @@
     path.lineTo(407, 119);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(471.228394f, 64.443222f);
     pathB.cubicTo(471.193878f, 60.953373f, 470.234985f, 52.4797592f, 462.201569f, 46.6231461f);
     pathB.cubicTo(454.168152f, 40.7665405f, 446.592804f, 41.993145f, 443.033936f, 42.8568878f);
@@ -2606,7 +2606,7 @@
 
 static void skpwww_seopack_blogspot_com_2153(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(999.892212f, 246);
     path.lineTo(927.340759f, 245.505722f);
     path.quadTo(928.068054f, 246, 929, 246);
@@ -2619,7 +2619,7 @@
     path.lineTo(1000, 248);
     path.lineTo(1000, 246);
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(924, 248);
     pathB.lineTo(924, 245.472672f);
     pathB.lineTo(1143, 247);
@@ -2629,7 +2629,7 @@
 
 static void skpwww_lokado_de_173(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 896.991394f);
     path.quadTo(999.789917f, 896.718872f, 999.535522f, 896.464478f);
     path.quadTo(998.071045f, 895, 996, 895);
@@ -2645,7 +2645,7 @@
     path.lineTo(1000, 896.991394f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(946, 906);
     pathB.lineTo(946, 905);
     pathB.quadTo(946, 901.272095f, 948.928955f, 898.636047f);
@@ -2665,7 +2665,7 @@
 
 static void skpwww_wartepop_blogspot_com_br_6(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(90.9763107f, 153.309662f);
     path.quadTo(91.9526215f, 152.333344f, 93.3333359f, 152.333344f);
     path.lineTo(124.666664f, 152.333344f);
@@ -2677,7 +2677,7 @@
     path.quadTo(90, 154.285965f, 90.9763107f, 153.309662f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(90, 163.666672f);
     pathB.lineTo(90, 155.666672f);
     pathB.quadTo(90, 154.285965f, 90.9763107f, 153.309662f);
@@ -2697,7 +2697,7 @@
 
 static void skpwww_wartepop_blogspot_com_br_6a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(90.9763107f, 153.309662f);
     path.quadTo(91.9526215f, 152.333344f, 93.3333359f, 152.333344f);
     path.lineTo(124.666672f, 152.333344f);
@@ -2709,7 +2709,7 @@
     path.quadTo(90, 154.285965f, 90.9763107f, 153.309662f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(90, 163.666672f);
     pathB.lineTo(90, 155.666672f);
     pathB.quadTo(90, 154.285965f, 90.9763107f, 153.309662f);
@@ -2729,7 +2729,7 @@
 
 static void skpwww_odia_com_br_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(360.740479f, 741.040771f);
     path.quadTo(360.378967f, 741, 360, 741);
     path.quadTo(359.159821f, 741, 358.403076f, 741.200745f);
@@ -2765,7 +2765,7 @@
     path.quadTo(361.102509f, 741.089966f, 360.740479f, 741.040771f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(355.654724f, 739.711792f);
     pathB.lineTo(367.288269f, 742.654724f);
     pathB.lineTo(364.345337f, 754.288269f);
@@ -2776,7 +2776,7 @@
 
 static void skpwww_evolvehq_com_210(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(172, 972);
     path.quadTo(170.757355f, 972, 169.878677f, 972.878662f);
     path.quadTo(169, 973.757385f, 169, 975);
@@ -2792,7 +2792,7 @@
     path.lineTo(172, 972);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(170, 1171);
     pathB.lineTo(170, 975);
     pathB.quadTo(170, 974.17157f, 170.585785f, 973.585815f);
@@ -2812,7 +2812,7 @@
 
 static void skpwww_catingueiraonline_com_352(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(443, 8292);
     path.lineTo(443, 8140);
     path.lineTo(444, 8140);
@@ -2820,7 +2820,7 @@
     path.lineTo(443, 8292);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(443, 8140);
     pathB.lineTo(444.01001f, 8140);
     pathB.lineTo(444, 8292);
@@ -2830,7 +2830,7 @@
 
 static void skpwww_galaxystwo_com_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(10105, 2510);
     path.lineTo(10123, 2509.98999f);
     path.lineTo(10123, 2511);
@@ -2838,7 +2838,7 @@
     path.lineTo(10105, 2510);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(10105, 2511);
     pathB.lineTo(10105, 2509.98999f);
     pathB.lineTo(10123, 2510);
@@ -2848,7 +2848,7 @@
 
 static void skpwww_thaienews_blogspot_com_36(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(429.994995f, 6268);
     path.lineTo(430, 2187);
     path.lineTo(430.5f, 2187);
@@ -2856,7 +2856,7 @@
     path.lineTo(429.994995f, 6268);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(430.5f, 2187);
     pathB.lineTo(429.994995f, 2187);
     pathB.lineTo(430, 6268);
@@ -2866,7 +2866,7 @@
 
 static void skpwww_fashionscandal_com_94(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(25.9107456f, 272.577423f);
     path.quadTo(26.1548233f, 272.333344f, 26.5000019f, 272.333344f);
     path.lineTo(131.166672f, 272.333344f);
@@ -2882,7 +2882,7 @@
     path.quadTo(25.6666679f, 272.821503f, 25.9107456f, 272.577423f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(25.833334f, 417.166656f);
     pathB.lineTo(25.833334f, 273.166656f);
     pathB.quadTo(25.833334f, 272.890533f, 26.0285969f, 272.695251f);
@@ -2902,7 +2902,7 @@
 
 static void skpwww_kenlevine_blogspot_com_28(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(276, 9506);
     path.lineTo(276, 7531);
     path.lineTo(277, 7531);
@@ -2910,7 +2910,7 @@
     path.lineTo(276, 9506);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(276, 7531);
     pathB.lineTo(277.01001f, 7531);
     pathB.lineTo(277, 9506);
@@ -2920,7 +2920,7 @@
 
 static void skpwww_defense_studies_blogspot_com_64(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(276, 9600);
     path.lineTo(276, 7703);
     path.lineTo(277, 7703);
@@ -2928,7 +2928,7 @@
     path.lineTo(276, 9600);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(276, 7703);
     pathB.lineTo(277.01001f, 7703);
     pathB.lineTo(277, 9600);
@@ -2938,7 +2938,7 @@
 
 static void skpwww_uniquefx_net_442(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(960, 306);
     path.lineTo(960, 305);
     path.lineTo(1000, 305);
@@ -2946,7 +2946,7 @@
     path.lineTo(960, 306);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(960, 305);
     pathB.lineTo(958.997253f, 306.002747f);
     pathB.lineTo(1017, 307);
@@ -2956,7 +2956,7 @@
 
 static void skpwww_kitcheninspirations_wordpress_com_32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(47.1666679f, 19651.334f);
     path.lineTo(65.8333359f, 19651.332f);
     path.lineTo(65.8333359f, 19651.5f);
@@ -2964,7 +2964,7 @@
     path.lineTo(47.1666679f, 19651.334f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(47.1666679f, 19651.5f);
     pathB.lineTo(47.1666679f, 19651.332f);
     pathB.lineTo(65.8333359f, 19651.334f);
@@ -2974,7 +2974,7 @@
 
 static void skpwww_educationalcraft_com_4(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(941, 1494);
     path.lineTo(941, 1464);
     path.lineTo(985, 1464);
@@ -2982,7 +2982,7 @@
     path.lineTo(941, 1494);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(979.211975f, 1480.45496f);
     pathB.cubicTo(979.211975f, 1480.45496f, 976.348999f, 1479.68506f, 977.495972f, 1475.59497f);
     pathB.cubicTo(977.497009f, 1475.59497f, 981.072021f, 1477.88501f, 979.211975f, 1480.45496f);
@@ -3152,7 +3152,7 @@
 
 static void skpwww_narayana_publishers_com_194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1083.34314f, 445.65686f);
     path.quadTo(1081, 443.313721f, 1081, 440);
     path.lineTo(1257, 440);
@@ -3170,7 +3170,7 @@
     path.quadTo(1083, 443.071075f, 1083, 441);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1081, 440);
     pathB.lineTo(1082, 440);
     pathB.lineTo(1090.01001f, 448);
@@ -3180,7 +3180,7 @@
 
 static void skpwww_cooksnaps_com_17(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(170.340179f, 176);
     path.lineTo(166, 176);
     path.quadTo(161.964188f, 176, 158.299957f, 176.896912f);
@@ -3209,7 +3209,7 @@
     path.quadTo(172.503067f, 176.282898f, 170.340179f, 176);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(139.4702f, 223.683014f);
     pathB.lineTo(138.9702f, 222.816986f);
     pathB.quadTo(132.549896f, 211.696686f, 135.873291f, 199.293594f);
@@ -3229,7 +3229,7 @@
 
 static void skpwww_swapspacesystems_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(819.050781f, 5539.72412f);
     path.quadTo(819.651672f, 5539.1543f, 820.479858f, 5539.17578f);
     path.lineTo(1191.35278f, 5548.8877f);
@@ -3245,7 +3245,7 @@
     path.quadTo(818.44989f, 5540.29492f, 819.050781f, 5539.72412f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(818.48053f, 5539.12354f);
     pathB.lineTo(1193.35205f, 5548.93994f);
     pathB.lineTo(1186.5199f, 5809.85059f);
@@ -3256,7 +3256,7 @@
 
 static void skpwww_kitcheninspirations_wordpress_com_66(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(47.1666679f, 27820.668f);
     path.lineTo(60.8333359f, 27820.668f);
     path.lineTo(60.8333359f, 27820.498f);
@@ -3264,7 +3264,7 @@
     path.lineTo(47.1666679f, 27820.668f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(47.1666679f, 27820.668f);
     pathB.lineTo(47.1666679f, 27820.498f);
     pathB.lineTo(60.8333359f, 27820.5f);
@@ -3274,7 +3274,7 @@
 
 static void skpwww_etiqadd_com_2464(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(630.378662f, 1293.42896f);
     path.quadTo(631.257385f, 1292.55029f, 632.5f, 1292.55029f);
     path.quadTo(633.742615f, 1292.55029f, 634.621338f, 1293.42896f);
@@ -3290,7 +3290,7 @@
     path.lineTo(630.378662f, 1293.42896f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(632.5f, 1291.30762f);
     pathB.lineTo(641.692383f, 1300.5f);
     pathB.lineTo(632.5f, 1309.69238f);
@@ -3301,7 +3301,7 @@
 
 static void skpwww_narayana_verlag_de_194(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1083.34314f, 513.65686f);
     path.quadTo(1081, 511.313721f, 1081, 508);
     path.lineTo(1257, 508);
@@ -3319,7 +3319,7 @@
     path.quadTo(1083, 511.071075f, 1083, 509);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(1081, 508);
     pathB.lineTo(1082, 508);
     pathB.lineTo(1090.01001f, 516);
@@ -3329,7 +3329,7 @@
 
 static void skpwww_americascup_com_108(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(999.454102f, 689.17157f);
     path.quadTo(1001.172f, 688, 1002.82886f, 688);
     path.lineTo(1013.82886f, 688);
@@ -3341,7 +3341,7 @@
     path.quadTo(997.736206f, 690.34314f, 999.454102f, 689.17157f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(998.828857f, 688);
     pathB.lineTo(1013.82886f, 688);
     pathB.lineTo(1002.17114f, 713);
@@ -3352,7 +3352,7 @@
 
 static void skpwww_vantageproduction_com_109(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(794.514709f, 759.485291f);
     path.quadTo(791, 755.970581f, 791, 751);
     path.lineTo(1133, 751);
@@ -3370,7 +3370,7 @@
     path.quadTo(793, 755.727905f, 793, 752);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(791, 751);
     pathB.lineTo(792, 751);
     pathB.lineTo(804.01001f, 763);
@@ -3380,7 +3380,7 @@
 
 static void skpwww_aceinfographics_com_106(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(166.878677f, 7638.87891f);
     path.quadTo(166, 7639.75732f, 166, 7641);
     path.lineTo(166, 11577);
@@ -3394,7 +3394,7 @@
     path.lineTo(166.878677f, 7638.87891f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(166, 7638);
     pathB.lineTo(168.020004f, 7635.97998f);
     pathB.lineTo(168, 11578);
@@ -3404,7 +3404,7 @@
 
 static void skpwww_tcmevents_org_13(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(465.951904f, 547.960144f);
     path.quadTo(465.66571f, 546.867371f, 465.404938f, 546);
     path.lineTo(465.504089f, 546);
@@ -3420,7 +3420,7 @@
     path.quadTo(474.905701f, 601.569519f, 470.591064f, 574.024353f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(322.992462f, 541.475708f);
     pathB.lineTo(465.531616f, 541.724426f);
     pathB.lineTo(468.507751f, 560.724426f);
@@ -3431,7 +3431,7 @@
 
 static void skpwww_paseoitaigara_com_br_56(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(633.147217f, 1247);
     path.lineTo(718, 1162.14722f);
     path.lineTo(802.852783f, 1247);
@@ -3439,7 +3439,7 @@
     path.lineTo(633.147217f, 1247);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(635.268494f, 1244.87866f);
     pathB.lineTo(715.878662f, 1164.26855f);
     pathB.quadTo(716.757385f, 1163.38989f, 718, 1163.38989f);
@@ -3459,7 +3459,7 @@
 
 static void skpwww_mortgagemarketguide_com_109(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(816.514709f, 781.485291f);
     path.quadTo(813, 777.970581f, 813, 773);
     path.lineTo(1133, 773);
@@ -3477,7 +3477,7 @@
     path.quadTo(815, 777.727905f, 815, 774);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(813, 773);
     pathB.lineTo(814, 773);
     pathB.lineTo(826.01001f, 785);
@@ -3487,7 +3487,7 @@
 
 static void skpwww_9to5mac_com_64(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(365, 5101);
     path.lineTo(365, 5082);
     path.lineTo(366, 5083);
@@ -3499,7 +3499,7 @@
     path.quadTo(365, 5103.07129f, 365, 5101);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(365, 5082);
     pathB.lineTo(365.848175f, 5081.15186f);
     pathB.lineTo(368, 5103);
@@ -3509,7 +3509,7 @@
 
 static void skpwww_googleventures_com_32(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(725.911682f, 898.767456f);
     path.lineTo(741.232544f, 885.911682f);
     path.lineTo(754.088318f, 901.232544f);
@@ -3517,7 +3517,7 @@
     path.lineTo(725.911682f, 898.767456f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(728.37677f, 870.59082f);
     pathB.lineTo(754.088257f, 901.232605f);
     pathB.lineTo(738.767395f, 914.088379f);
@@ -3528,7 +3528,7 @@
 
 static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(4915, 1523);
     path.quadTo(4887.24756f, 1523, 4867.62402f, 1542.6239f);
     path.quadTo(4848, 1562.24768f, 4848, 1590);
@@ -3540,7 +3540,7 @@
     path.quadTo(4942.75244f, 1523, 4915, 1523);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(4981.99902f, 1590);
     pathB.quadTo(4981.99902f, 1617.75232f, 4962.375f, 1637.3761f);
     pathB.quadTo(4942.75146f, 1657, 4914.99902f, 1657);
@@ -3556,7 +3556,7 @@
 
 static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(10210.8789f, 5315.87891f);
     path.quadTo(10211.7578f, 5315, 10213, 5315);
     path.lineTo(10230, 5315);
@@ -3572,7 +3572,7 @@
     path.quadTo(10210, 5316.75732f, 10210.8789f, 5315.87891f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(10213, 5315);
     pathB.lineTo(10230, 5315);
     pathB.cubicTo(10231.6572f, 5315, 10233, 5316.34326f, 10233, 5318);
@@ -3588,7 +3588,7 @@
 
 static void skpwww_familysurvivalprotocol_wordpress_com_61(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(143, 14557);
     path.lineTo(165, 14557);
     path.lineTo(165, 14555.9902f);
@@ -3596,7 +3596,7 @@
     path.lineTo(143, 14557);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(143, 14557);
     pathB.lineTo(143, 14555.9902f);
     pathB.lineTo(165, 14556);
@@ -3606,7 +3606,7 @@
 
 static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(808.585815f, 11673.5859f);
     path.quadTo(809.17157f, 11673, 810, 11673);
     path.lineTo(1032, 11673);
@@ -3632,7 +3632,7 @@
     path.quadTo(808, 11674.1719f, 808.585815f, 11673.5859f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(808, 11703);
     pathB.lineTo(809.5f, 11701.5f);
     pathB.lineTo(1062.91907f, 11687.0811f);
@@ -3642,7 +3642,7 @@
 
 static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(205.884888f, 648.203857f);
     path.lineTo(771.570374f, 82.5183716f);
     path.lineTo(1110.98169f, 421.929626f);
@@ -3650,7 +3650,7 @@
     path.lineTo(205.884888f, 648.203857f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(771.570374f, 82.5183716f);
     pathB.lineTo(1110.98169f, 421.929626f);
     pathB.lineTo(545.296204f, 987.615051f);
@@ -3661,7 +3661,7 @@
 
 static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(78.6429825f, 1394.30969f);
     path.quadTo(79.6192932f, 1393.33337f, 81.0000076f, 1393.33337f);
     path.lineTo(341, 1393.33337f);
@@ -3677,7 +3677,7 @@
     path.quadTo(77.6666718f, 1395.28601f, 78.6429825f, 1394.30969f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(81, 1393.33337f);
     pathB.lineTo(341, 1393.33337f);
     pathB.cubicTo(342.840942f, 1393.33337f, 344.333344f, 1394.82568f, 344.333344f, 1396.66675f);
@@ -3693,7 +3693,7 @@
 
 static void skpwww_shinydemos_com_15(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(1000, 310.947968f);
     path.lineTo(771.570374f, 82.5183716f);
     path.lineTo(205.884888f, 648.203857f);
@@ -3703,7 +3703,7 @@
     path.lineTo(1000, 310.947968f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
     pathB.moveTo(771.570374f, 82.5183716f);
     pathB.lineTo(1110.98169f, 421.929626f);
     pathB.lineTo(545.296204f, 987.615051f);
@@ -3714,7 +3714,7 @@
 
 static void skpwww_lptemp_com_5(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(78.6429825f, 3150.97632f);
     path.quadTo(79.6192932f, 3150, 81.0000076f, 3150);
     path.lineTo(341, 3150);
@@ -3730,7 +3730,7 @@
     path.quadTo(77.6666718f, 3151.95264f, 78.6429825f, 3150.97632f);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.setFillType(SkPathFillType::kEvenOdd);
     pathB.moveTo(81, 3150);
     pathB.lineTo(341, 3150);
     pathB.cubicTo(342.840942f, 3150, 344.333344f, 3151.49268f, 344.333344f, 3153.3335f);
@@ -3746,7 +3746,7 @@
 
 static void skpwww_educationalcraft_com_4a(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(941, 1494);
     path.lineTo(941, 1464);
     path.lineTo(985, 1464);
@@ -3754,7 +3754,7 @@
     path.lineTo(941, 1494);
     path.close();
     SkPath pathB;
-    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.setFillType(SkPathFillType::kWinding);
 
 pathB.moveTo(984.546021f, 1478.31494f);
 pathB.cubicTo(984.546021f, 1478.31494f, 984.543213f, 1478.32239f, 984.537598f, 1478.33655f);
@@ -3767,7 +3767,7 @@
 
 static void skpwww_woothemes_com_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d));  // 796.618f, 3533.72f
 path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455cdb8d));  // 793.94f, 3533.72f
 path.lineTo(SkBits2Float(0x44467c27), SkBits2Float(0x455d055d));  // 793.94f, 3536.34f
@@ -3775,7 +3775,7 @@
 path.lineTo(SkBits2Float(0x44472795), SkBits2Float(0x455cdb8d));  // 796.618f, 3533.72f
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x4446861c), SkBits2Float(0x455cdb8d));  // 794.095f, 3533.72f
 path.cubicTo(SkBits2Float(0x4446a0d8), SkBits2Float(0x455cefbb), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d), SkBits2Float(0x444727a5), SkBits2Float(0x455d055d));  // 794.513f, 3534.98f, 796.619f, 3536.34f, 796.619f, 3536.34f
 path.cubicTo(SkBits2Float(0x4446c5b0), SkBits2Float(0x455cf8a4), SkBits2Float(0x444693af), SkBits2Float(0x455cedad), SkBits2Float(0x44467c1b), SkBits2Float(0x455ce4b8));  // 795.089f, 3535.54f, 794.308f, 3534.85f, 793.939f, 3534.29f
@@ -3787,7 +3787,7 @@
 
 static void skpwww_gorcraft_ru_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x44924000), SkBits2Float(0x458e7800));  // 1170, 4559
 path.conicTo(SkBits2Float(0x44930000), SkBits2Float(0x458e7800), SkBits2Float(0x44930000), SkBits2Float(0x458ea800), SkBits2Float(0x3f3504f3));  // 1176, 4559, 1176, 4565, 0.707107f
 path.lineTo(SkBits2Float(0x44930000), SkBits2Float(0x458f7000));  // 1176, 4590
@@ -3800,7 +3800,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x429c0000), SkBits2Float(0x458f7000));  // 78, 4590
 path.lineTo(SkBits2Float(0x429c0000), SkBits2Float(0x458ea800));  // 78, 4565
 path.conicTo(SkBits2Float(0x429c0000), SkBits2Float(0x458e7800), SkBits2Float(0x42a80000), SkBits2Float(0x458e7800), SkBits2Float(0x3f3504f3));  // 78, 4559, 84, 4559, 0.707107f
@@ -3818,7 +3818,7 @@
 
 static void skpwww_neda_net_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x447a0000), SkBits2Float(0x00000000));  // 1000, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000));  // 0, 0
 path.lineTo(SkBits2Float(0x00000000), SkBits2Float(0x44b6e000));  // 0, 1463
@@ -3827,7 +3827,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x366a410f), SkBits2Float(0x43a38000));  // 3.49066e-06f, 327
 path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x43a38001));  // 1000, 327
 path.lineTo(SkBits2Float(0x447a0000), SkBits2Float(0x4435c000));  // 1000, 727
@@ -3841,7 +3841,7 @@
 // "http___www_neda_net.skp" dir=87
 static void skpwww_neda_net_2(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x442fc000), SkBits2Float(0x4546a000));  // 703, 3178
 path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x4546a000));  // 637, 3178
 path.lineTo(SkBits2Float(0x441f4000), SkBits2Float(0x454ab000));  // 637, 3243
@@ -3850,7 +3850,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x44220e6e), SkBits2Float(0x45469c4c));  // 648.225f, 3177.77f
 path.lineTo(SkBits2Float(0x442fc01c), SkBits2Float(0x45475696));  // 703.002f, 3189.41f
 path.lineTo(SkBits2Float(0x442cf191), SkBits2Float(0x454aa3b5));  // 691.774f, 3242.23f
@@ -3863,7 +3863,7 @@
 
 static void skpwww_mybuilder_com_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(1000, 659);
 path.lineTo(1000, 377);
 path.lineTo(455, 377);
@@ -3872,7 +3872,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(921.472f, 414.086f);
 path.lineTo(968.815f, 386.754f);
 path.lineTo(993.069f, 428.761f);
@@ -3920,7 +3920,7 @@
 
 static void skpwww_nimble_com_au_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(188.6f, 1988.8f);
 path.lineTo(188.6f, 2041.6f);
 path.cubicTo(188.6f, 2065.4f, 208, 2084.8f, 231.8f, 2084.8f);
@@ -3931,7 +3931,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(275, 2041.6f);
 path.conicTo(275, 2084.8f, 231.8f, 2084.8f, 0.707107f);
 path.conicTo(188.6f, 2084.8f, 188.6f, 2041.6f, 0.707107f);
@@ -3944,7 +3944,7 @@
 
 static void skpwww_tinytots_com_1(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-path.setFillType(SkPath::kEvenOdd_FillType);
+path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(75.96f, 26.318f);
 path.lineTo(70.337f, 26.318f);
 path.lineTo(70.337f, 32.376f);
@@ -3953,7 +3953,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-path.setFillType(SkPath::kWinding_FillType);
+path.setFillType(SkPathFillType::kWinding);
 path.moveTo(75.88f, 27.873f);
 path.cubicTo(75.929f, 28.138f, 75.956f, 29.196f, 75.96f, 31.046f);
 path.lineTo(72.766f, 32.376f);
@@ -3975,7 +3975,7 @@
 
 static void http___www_emuleteca_cl_26(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x44370000));  // 1, 732
 path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x42680000), SkBits2Float(0x4428c000), SkBits2Float(0x3f3504f3));  // 1, 675, 58, 675, 0.707107f
 path.conicTo(SkBits2Float(0x40400000), SkBits2Float(0x4428c000), SkBits2Float(0x40400000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 3, 675, 3, 732, 0.707107f
@@ -3991,7 +3991,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
 path.lineTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000));  // 58, 675
 path.lineTo(SkBits2Float(0x3fc8f676), SkBits2Float(0x44454000));  // 1.57002f, 789
@@ -4003,7 +4003,7 @@
 
 static void http___www_emuleteca_cl_27(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType((SkPath::FillType) 1);
+    path.setFillType((SkPathFillType) 1);
 path.moveTo(SkBits2Float(0x42680000), SkBits2Float(0x4428c000));  // 58, 675
 path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000), SkBits2Float(0x3f800000), SkBits2Float(0x44370000), SkBits2Float(0x3f3504f3));  // 1, 675, 1, 732, 0.707107f
 path.conicTo(SkBits2Float(0x3f800000), SkBits2Float(0x443bd045), SkBits2Float(0x414acf56), SkBits2Float(0x443fa420), SkBits2Float(0x3f778612));  // 1, 751.254f, 12.6756f, 766.564f, 0.96689f
@@ -4014,7 +4014,7 @@
 
     SkPath path1(path);
     path.reset();
-    path.setFillType((SkPath::FillType) 0);
+    path.setFillType((SkPathFillType) 0);
 path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
 path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89));  // 5.30165e+07f, -1.06032e+08f
 path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec));  // 20.8834f, 749.233f
@@ -4026,11 +4026,11 @@
 
 static void http___www_emuleteca_cl_28(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 
     SkPath path1(path);
     path.reset();
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
 path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x4428c000));  // 1, 675
 path.lineTo(SkBits2Float(0x4c4a3de6), SkBits2Float(0xccca3d89));  // 5.30165e+07f, -1.06032e+08f
 path.lineTo(SkBits2Float(0x41a71147), SkBits2Float(0x443b4eec));  // 20.8834f, 749.233f
@@ -4041,7 +4041,7 @@
 
 static void http___www_project2061_org(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
 path.moveTo(751, 62);
 path.lineTo(497, 62);
 path.lineTo(497, 138);
@@ -4050,7 +4050,7 @@
 path.close();
     SkPath path1(path);
     path.reset();
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
 path.moveTo(699.889f, 115.29f);
 path.cubicTo(699.889f, 114.913f, 699.627f, 114.723f, 699.111f, 114.723f);
 path.cubicTo(698.604f, 114.723f, 697.843f, 114.671f, 697.096f, 114.423f);
diff --git a/tests/PathOpsThreadedCommon.h b/tests/PathOpsThreadedCommon.h
index 4b4c9b8..1248fde 100644
--- a/tests/PathOpsThreadedCommon.h
+++ b/tests/PathOpsThreadedCommon.h
@@ -34,7 +34,7 @@
     skiatest::Reporter* fReporter;
     SkBitmap* fBitmap;
 
-    void outputProgress(const char* pathStr, SkPath::FillType);
+    void outputProgress(const char* pathStr, SkPathFillType);
     void outputProgress(const char* pathStr, SkPathOp);
 };
 
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 54ef767..611e0a6 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -1129,13 +1129,13 @@
     // Test two donuts, each wound a different direction. Only the outer contour
     // determines the cheap direction
     path.reset();
-    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
-    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(2), SkPathDirection::kCW);
+    path.addCircle(0, 0, SkIntToScalar(1), SkPathDirection::kCCW);
     check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
 
     path.reset();
-    path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
-    path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(1), SkPathDirection::kCW);
+    path.addCircle(0, 0, SkIntToScalar(2), SkPathDirection::kCCW);
     check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 
     // triangle with one point really far from the origin.
@@ -1155,7 +1155,7 @@
     path.lineTo(1, 1e7f);
     path.lineTo(1e7f, 2e7f);
     path.close();
-    REPORTER_ASSERT(reporter, SkPath::kConvex_Convexity == path.getConvexity());
+    REPORTER_ASSERT(reporter, path.isConvex());
     check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 }
 
@@ -1317,11 +1317,11 @@
 }
 
 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
-                            SkPath::Convexity expected) {
+                            SkPathConvexityType expected) {
     SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
-    SkPath::Convexity c = copy.getConvexity();
+    SkPathConvexityType c = copy.getConvexityType();
     REPORTER_ASSERT(reporter, c == expected);
-#ifndef SK_LEGACY_PATH_CONVEXITY
+
     // test points-by-array interface
     SkPath::Iter iter(path, true);
     int initialMoves = 0;
@@ -1335,9 +1335,8 @@
         (void) path.getPoints(&points.front(), points.size());
         int skip = initialMoves - 1;
         bool isConvex = SkPathPriv::IsConvex(&points.front() + skip, points.size() - skip);
-        REPORTER_ASSERT(reporter, isConvex == (SkPath::kConvex_Convexity == expected));
+        REPORTER_ASSERT(reporter, isConvex == (SkPathConvexityType::kConvex == expected));
     }
-#endif
 }
 
 static void test_path_crbug389050(skiatest::Reporter* reporter) {
@@ -1347,10 +1346,10 @@
     tinyConvexPolygon.lineTo(600.148962f, 800.142338f);
     tinyConvexPolygon.lineTo(600.134891f, 800.137724f);
     tinyConvexPolygon.close();
-    tinyConvexPolygon.getConvexity();
+    tinyConvexPolygon.getConvexityType();
     // This is convex, but so small that it fails many of our checks, and the three "backwards"
     // bends convince the checker that it's concave. That's okay though, we draw it correctly.
-    check_convexity(reporter, tinyConvexPolygon, SkPath::kConcave_Convexity);
+    check_convexity(reporter, tinyConvexPolygon, SkPathConvexityType::kConcave);
     check_direction(reporter, tinyConvexPolygon, SkPathPriv::kCW_FirstDirection);
 
     SkPath  platTriangle;
@@ -1358,7 +1357,7 @@
     platTriangle.lineTo(200, 0);
     platTriangle.lineTo(100, 0.04f);
     platTriangle.close();
-    platTriangle.getConvexity();
+    platTriangle.getConvexityType();
     check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
 
     platTriangle.reset();
@@ -1366,7 +1365,7 @@
     platTriangle.lineTo(200, 0);
     platTriangle.lineTo(100, 0.03f);
     platTriangle.close();
-    platTriangle.getConvexity();
+    platTriangle.getConvexityType();
     check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
 }
 
@@ -1374,14 +1373,14 @@
     SkPath pt;
     pt.moveTo(0, 0);
     pt.close();
-    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
+    check_convexity(reporter, pt, SkPathConvexityType::kConvex);
     check_direction(reporter, pt, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath line;
     line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
     line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
     line.close();
-    check_convexity(reporter, line, SkPath::kConvex_Convexity);
+    check_convexity(reporter, line, SkPathConvexityType::kConvex);
     check_direction(reporter, line, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath triLeft;
@@ -1389,7 +1388,7 @@
     triLeft.lineTo(SK_Scalar1, 0);
     triLeft.lineTo(SK_Scalar1, SK_Scalar1);
     triLeft.close();
-    check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
+    check_convexity(reporter, triLeft, SkPathConvexityType::kConvex);
     check_direction(reporter, triLeft, SkPathPriv::kCW_FirstDirection);
 
     SkPath triRight;
@@ -1397,7 +1396,7 @@
     triRight.lineTo(-SK_Scalar1, 0);
     triRight.lineTo(SK_Scalar1, SK_Scalar1);
     triRight.close();
-    check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
+    check_convexity(reporter, triRight, SkPathConvexityType::kConvex);
     check_direction(reporter, triRight, SkPathPriv::kCCW_FirstDirection);
 
     SkPath square;
@@ -1406,7 +1405,7 @@
     square.lineTo(SK_Scalar1, SK_Scalar1);
     square.lineTo(0, SK_Scalar1);
     square.close();
-    check_convexity(reporter, square, SkPath::kConvex_Convexity);
+    check_convexity(reporter, square, SkPathConvexityType::kConvex);
     check_direction(reporter, square, SkPathPriv::kCW_FirstDirection);
 
     SkPath redundantSquare;
@@ -1423,7 +1422,7 @@
     redundantSquare.lineTo(0, SK_Scalar1);
     redundantSquare.lineTo(0, SK_Scalar1);
     redundantSquare.close();
-    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
+    check_convexity(reporter, redundantSquare, SkPathConvexityType::kConvex);
     check_direction(reporter, redundantSquare, SkPathPriv::kCW_FirstDirection);
 
     SkPath bowTie;
@@ -1440,7 +1439,7 @@
     bowTie.lineTo(0, SK_Scalar1);
     bowTie.lineTo(0, SK_Scalar1);
     bowTie.close();
-    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
+    check_convexity(reporter, bowTie, SkPathConvexityType::kConcave);
     check_direction(reporter, bowTie, kDontCheckDir);
 
     SkPath spiral;
@@ -1452,7 +1451,7 @@
     spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
     spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
     spiral.close();
-    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
+    check_convexity(reporter, spiral, SkPathConvexityType::kConcave);
     check_direction(reporter, spiral, kDontCheckDir);
 
     SkPath dent;
@@ -1462,7 +1461,7 @@
     dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
     dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
     dent.close();
-    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
+    check_convexity(reporter, dent, SkPathConvexityType::kConcave);
     check_direction(reporter, dent, SkPathPriv::kCW_FirstDirection);
 
     // https://bug.skia.org/2235
@@ -1479,7 +1478,7 @@
     SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
     stroke.setStrokeStyle(2 * SK_Scalar1);
     stroke.applyToPath(&strokedSin, strokedSin);
-    check_convexity(reporter, strokedSin, SkPath::kConcave_Convexity);
+    check_convexity(reporter, strokedSin, SkPathConvexityType::kConcave);
     check_direction(reporter, strokedSin, kDontCheckDir);
 
     // http://crbug.com/412640
@@ -1490,7 +1489,7 @@
     degenerateConcave.lineTo(41.446522f, 376.25f);
     degenerateConcave.lineTo(-55.971577f, 460.0f);
     degenerateConcave.lineTo(41.446522f, 376.25f);
-    check_convexity(reporter, degenerateConcave, SkPath::kConcave_Convexity);
+    check_convexity(reporter, degenerateConcave, SkPathConvexityType::kConcave);
     check_direction(reporter, degenerateConcave, SkPathPriv::kUnknown_FirstDirection);
 
     // http://crbug.com/433683
@@ -1505,7 +1504,7 @@
     badFirstVector.lineTo(504.912292f, 316.389648f);
     badFirstVector.lineTo(501.087708f, 319.610352f);
     badFirstVector.close();
-    check_convexity(reporter, badFirstVector, SkPath::kConcave_Convexity);
+    check_convexity(reporter, badFirstVector, SkPathConvexityType::kConcave);
 
     // http://crbug.com/993330
     SkPath falseBackEdge;
@@ -1520,30 +1519,30 @@
                           -151.46727226799754f,     -419.98027663161537f,
                           -217.83430557928145f,     -382.14948768484857f);
     falseBackEdge.close();
-    check_convexity(reporter, falseBackEdge, SkPath::kConcave_Convexity);
+    check_convexity(reporter, falseBackEdge, SkPathConvexityType::kConcave);
 }
 
 static void test_convexity_doubleback(skiatest::Reporter* reporter) {
     SkPath doubleback;
     doubleback.lineTo(1, 1);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.lineTo(2, 2);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.reset();
     doubleback.lineTo(1, 0);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.lineTo(2, 0);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.lineTo(1, 0);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.reset();
     doubleback.quadTo(1, 1, 2, 2);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.reset();
     doubleback.quadTo(1, 0, 2, 0);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
     doubleback.quadTo(1, 0, 0, 0);
-    check_convexity(reporter, doubleback, SkPath::kConvex_Convexity);
+    check_convexity(reporter, doubleback, SkPathConvexityType::kConvex);
 }
 
 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
@@ -1583,39 +1582,39 @@
 static void test_convexity(skiatest::Reporter* reporter) {
     SkPath path;
 
-    check_convexity(reporter, path, SkPath::kConvex_Convexity);
+    check_convexity(reporter, path, SkPathConvexityType::kConvex);
     path.addCircle(0, 0, SkIntToScalar(10));
-    check_convexity(reporter, path, SkPath::kConvex_Convexity);
+    check_convexity(reporter, path, SkPathConvexityType::kConvex);
     path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
-    check_convexity(reporter, path, SkPath::kConcave_Convexity);
+    check_convexity(reporter, path, SkPathConvexityType::kConcave);
 
     path.reset();
-    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
-    check_convexity(reporter, path, SkPath::kConvex_Convexity);
+    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPathDirection::kCCW);
+    check_convexity(reporter, path, SkPathConvexityType::kConvex);
     REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection));
 
     path.reset();
-    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
-    check_convexity(reporter, path, SkPath::kConvex_Convexity);
+    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPathDirection::kCW);
+    check_convexity(reporter, path, SkPathConvexityType::kConvex);
     REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection));
 
     path.reset();
     path.quadTo(100, 100, 50, 50); // This from GM:convexpaths
-    check_convexity(reporter, path, SkPath::kConvex_Convexity);
+    check_convexity(reporter, path, SkPathConvexityType::kConvex);
 
     static const struct {
         const char*                 fPathStr;
-        SkPath::Convexity           fExpectedConvexity;
+        SkPathConvexityType         fExpectedConvexity;
         SkPathPriv::FirstDirection  fExpectedDirection;
     } gRec[] = {
-        { "", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
-        { "0 0", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
-        { "0 0 10 10", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
-        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPathPriv::kUnknown_FirstDirection },
-        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPathPriv::kCW_FirstDirection },
-        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPathPriv::kCCW_FirstDirection },
-        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
-        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPathPriv::kCW_FirstDirection },
+        { "", SkPathConvexityType::kConvex, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0", SkPathConvexityType::kConvex, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10", SkPathConvexityType::kConvex, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 20 20 0 0 10 10", SkPathConvexityType::kConcave, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 10 20", SkPathConvexityType::kConvex, SkPathPriv::kCW_FirstDirection },
+        { "0 0 10 10 10 0", SkPathConvexityType::kConvex, SkPathPriv::kCCW_FirstDirection },
+        { "0 0 10 10 10 0 0 10", SkPathConvexityType::kConcave, kDontCheckDir },
+        { "0 0 10 0 0 10 -10 -10", SkPathConvexityType::kConcave, SkPathPriv::kCW_FirstDirection },
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
@@ -1633,7 +1632,7 @@
             REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir);
             check_convexity(reporter, copy, gRec[i].fExpectedConvexity);
         }
-        REPORTER_ASSERT(reporter, gRec[i].fExpectedConvexity == path.getConvexity());
+        REPORTER_ASSERT(reporter, gRec[i].fExpectedConvexity == path.getConvexityType());
         check_direction(reporter, path, gRec[i].fExpectedDirection);
     }
 
@@ -1682,7 +1681,7 @@
             case 11: path.cubicTo(nonFinitePts[i], axisAlignedPts[f], axisAlignedPts[g]); break;
             case 12: path.moveTo(nonFinitePts[i]); break;
         }
-        check_convexity(reporter, path, SkPath::kUnknown_Convexity);
+        check_convexity(reporter, path, SkPathConvexityType::kUnknown);
     }
 
     for (int index = 0; index < (int) (11 * axisAlignedPtsCount); ++index) {
@@ -1704,12 +1703,12 @@
             case 10: path.cubicTo(axisAlignedPts[g], axisAlignedPts[f], axisAlignedPts[g]); break;
         }
         if (curveSelect == 0 || curveSelect == 1 || curveSelect == 2 || curveSelect == 5) {
-            check_convexity(reporter, path, SkPath::kConvex_Convexity);
+            check_convexity(reporter, path, SkPathConvexityType::kConvex);
         } else {
             SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
-            SkPath::Convexity c = copy.getConvexity();
-            REPORTER_ASSERT(reporter, SkPath::kUnknown_Convexity == c
-                    || SkPath::kConcave_Convexity == c);
+            SkPathConvexityType c = copy.getConvexityType();
+            REPORTER_ASSERT(reporter, SkPathConvexityType::kUnknown == c
+                    || SkPathConvexityType::kConcave == c);
         }
     }
 
@@ -1739,12 +1738,12 @@
             case 10: path.cubicTo(diagonalPts[g], axisAlignedPts[f], diagonalPts[g]); break;
         }
         if (curveSelect == 0) {
-            check_convexity(reporter, path, SkPath::kConvex_Convexity);
+            check_convexity(reporter, path, SkPathConvexityType::kConvex);
         } else {
             SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
-            SkPath::Convexity c = copy.getConvexity();
-            REPORTER_ASSERT(reporter, SkPath::kUnknown_Convexity == c
-                    || SkPath::kConcave_Convexity == c);
+            SkPathConvexityType c = copy.getConvexityType();
+            REPORTER_ASSERT(reporter, SkPathConvexityType::kUnknown == c
+                    || SkPathConvexityType::kConcave == c);
         }
     }
 
@@ -1757,7 +1756,7 @@
     path.lineTo(SkBits2Float(0xbe917378), SkBits2Float(0xbd7ee1a9));  // -0.284084f, -0.0622269f
     path.lineTo(SkBits2Float(0xbe9171db), SkBits2Float(0xbd7eeb5d));  // -0.284072f, -0.0622362f
     path.close();
-    check_convexity(reporter, path, SkPath::kConcave_Convexity);
+    check_convexity(reporter, path, SkPathConvexityType::kConcave);
 
 }
 
@@ -1913,7 +1912,7 @@
                 swap(qRect.fTop, qRect.fBottom);
             }
             for (int d = 0; d < 2; ++d) {
-                SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+                SkPathDirection dir = d ? SkPathDirection::kCCW : SkPathDirection::kCW;
                 path.reset();
                 path.addRect(kBaseRect, dir);
                 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
@@ -2157,7 +2156,7 @@
         if (tests[testIndex].fIsRect) {
             SkRect computed, expected;
             bool isClosed;
-            SkPath::Direction direction;
+            SkPathDirection direction;
             SkPathPriv::FirstDirection cheapDirection;
             int pointCount = tests[testIndex].fPointCount - (d2 == tests[testIndex].fPoints);
             expected.setBounds(tests[testIndex].fPoints, pointCount);
@@ -2170,9 +2169,9 @@
             SkRect computed;
             computed.setLTRB(123, 456, 789, 1011);
             for (auto c : {true, false})
-            for (auto d : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+            for (auto d : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
               bool isClosed = c;
-              SkPath::Direction direction = d;
+              SkPathDirection direction = d;
               REPORTER_ASSERT(reporter, !path.isRect(&computed, &isClosed, &direction));
               REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
               REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
@@ -2239,9 +2238,9 @@
 }
 
 static void check_simple_closed_rect(skiatest::Reporter* reporter, const SkPath& path,
-                                     const SkRect& rect, SkPath::Direction dir, unsigned start) {
+                                     const SkRect& rect, SkPathDirection dir, unsigned start) {
     SkRect r = SkRect::MakeEmpty();
-    SkPath::Direction d = SkPath::kCCW_Direction;
+    SkPathDirection d = SkPathDirection::kCCW;
     unsigned s = ~0U;
 
     REPORTER_ASSERT(reporter, SkPathPriv::IsSimpleClosedRect(path, &r, &d, &s));
@@ -2253,14 +2252,14 @@
 static void test_is_simple_closed_rect(skiatest::Reporter* reporter) {
     using std::swap;
     SkRect r = SkRect::MakeEmpty();
-    SkPath::Direction d = SkPath::kCCW_Direction;
+    SkPathDirection d = SkPathDirection::kCCW;
     unsigned s = ~0U;
 
     const SkRect testRect = SkRect::MakeXYWH(10, 10, 50, 70);
     const SkRect emptyRect = SkRect::MakeEmpty();
     SkPath path;
     for (int start = 0; start < 4; ++start) {
-        for (auto dir : {SkPath::kCCW_Direction, SkPath::kCW_Direction}) {
+        for (auto dir : {SkPathDirection::kCCW, SkPathDirection::kCW}) {
             SkPath path;
             path.addRect(testRect, dir, start);
             check_simple_closed_rect(reporter, path, testRect, dir, start);
@@ -2317,9 +2316,9 @@
             path2.addRect(degenRect, dir, start);
             REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
             // An inverted rect makes a rect path, but changes the winding dir and start point.
-            SkPath::Direction swapDir = (dir == SkPath::kCW_Direction)
-                                            ? SkPath::kCCW_Direction
-                                            : SkPath::kCW_Direction;
+            SkPathDirection swapDir = (dir == SkPathDirection::kCW)
+                                            ? SkPathDirection::kCCW
+                                            : SkPathDirection::kCW;
             static constexpr unsigned kXSwapStarts[] = { 1, 0, 3, 2 };
             static constexpr unsigned kYSwapStarts[] = { 3, 2, 1, 0 };
             SkRect swapRect = testRect;
@@ -2341,7 +2340,7 @@
     path.lineTo(1, 1);
     path.lineTo(0, 1);
     SkRect rect;
-    SkPath::Direction  dir;
+    SkPathDirection  dir;
     unsigned start;
     path.close();
     REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
@@ -2444,7 +2443,7 @@
         for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
             SkPath path;
             if (rectFirst) {
-                path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+                path.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
             }
             path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
             for (index = 1; index < tests[testIndex].fPointCount; ++index) {
@@ -2454,14 +2453,14 @@
                 path.close();
             }
             if (!rectFirst) {
-                path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+                path.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
             }
             REPORTER_ASSERT(reporter,
                             tests[testIndex].fIsNestedRect == SkPathPriv::IsNestedFillRects(path, nullptr));
             if (tests[testIndex].fIsNestedRect) {
                 SkRect expected[2], computed[2];
                 SkPathPriv::FirstDirection expectedDirs[2];
-                SkPath::Direction computedDirs[2];
+                SkPathDirection computedDirs[2];
                 SkRect testBounds;
                 testBounds.setBounds(tests[testIndex].fPoints, tests[testIndex].fPointCount);
                 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
@@ -2483,7 +2482,7 @@
         // fail, close then line
         SkPath path1;
         if (rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
         for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
@@ -2492,14 +2491,14 @@
         path1.close();
         path1.lineTo(1, 0);
         if (!rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
         }
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
 
         // fail, move in the middle
         path1.reset();
         if (rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
         for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
@@ -2510,14 +2509,14 @@
         }
         path1.close();
         if (!rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
         }
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
 
         // fail, move on the edge
         path1.reset();
         if (rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
         }
         for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
             path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
@@ -2525,14 +2524,14 @@
         }
         path1.close();
         if (!rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
         }
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
 
         // fail, quad
         path1.reset();
         if (rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
         for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
@@ -2543,14 +2542,14 @@
         }
         path1.close();
         if (!rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
         }
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
 
         // fail, cubic
         path1.reset();
         if (rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
         }
         path1.moveTo(r1[0].fX, r1[0].fY);
         for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
@@ -2561,14 +2560,14 @@
         }
         path1.close();
         if (!rectFirst) {
-            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
         }
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
 
         // fail,  not nested
         path1.reset();
-        path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
-        path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
+        path1.addRect(1, 1, 3, 3, SkPathDirection::kCW);
+        path1.addRect(2, 2, 4, 4, SkPathDirection::kCW);
         REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
     }
 
@@ -2588,7 +2587,7 @@
 
     // pass, stroke rect
     SkPath src, dst;
-    src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
+    src.addRect(1, 1, 7, 7, SkPathDirection::kCW);
     SkPaint strokePaint;
     strokePaint.setStyle(SkPaint::kStroke_Style);
     strokePaint.setStrokeWidth(2);
@@ -2610,11 +2609,11 @@
     reader.readPath(&readBack);
     REPORTER_ASSERT(reporter, readBack == p);
 
-    REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
-                              p.getConvexityOrUnknown());
+    REPORTER_ASSERT(reporter, readBack.getConvexityTypeOrUnknown() ==
+                              p.getConvexityTypeOrUnknown());
 
     SkRect oval0, oval1;
-    SkPath::Direction dir0, dir1;
+    SkPathDirection dir0, dir1;
     unsigned start0, start1;
     REPORTER_ASSERT(reporter, readBack.isOval(nullptr) == p.isOval(nullptr));
     if (SkPathPriv::IsOval(p, &oval0, &dir0, &start0) &&
@@ -2756,7 +2755,7 @@
     }
 
     p.reset();
-    p.addCircle(0, 0, 1, SkPath::kCW_Direction);
+    p.addCircle(0, 0, 1, SkPathDirection::kCW);
 
     {
         SkMatrix matrix;
@@ -3286,7 +3285,7 @@
                              SkPathPriv::FirstDirection expectedDir) {
     SkRect rect = SkRect::MakeEmpty();
     REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
-    SkPath::Direction isOvalDir;
+    SkPathDirection isOvalDir;
     unsigned isOvalStart;
     if (SkPathPriv::IsOval(path, &rect, &isOvalDir, &isOvalStart)) {
         REPORTER_ASSERT(reporter, rect.height() == rect.width());
@@ -3406,7 +3405,7 @@
 }
 
 static void test_circle_with_direction(skiatest::Reporter* reporter,
-                                       SkPath::Direction inDir) {
+                                       SkPathDirection inDir) {
     const SkPathPriv::FirstDirection dir = SkPathPriv::AsFirstDirection(inDir);
     SkPath path;
 
@@ -3453,12 +3452,12 @@
     SkPath rect;
     SkPath empty;
 
-    const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
-    const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
+    const SkPathDirection kCircleDir = SkPathDirection::kCW;
+    const SkPathDirection kCircleDirOpposite = SkPathDirection::kCCW;
 
     circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
     rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
-                 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
+                 SkIntToScalar(20), SkIntToScalar(20), SkPathDirection::kCW);
 
     SkMatrix translate;
     translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
@@ -3484,24 +3483,24 @@
 }
 
 static void test_circle(skiatest::Reporter* reporter) {
-    test_circle_with_direction(reporter, SkPath::kCW_Direction);
-    test_circle_with_direction(reporter, SkPath::kCCW_Direction);
+    test_circle_with_direction(reporter, SkPathDirection::kCW);
+    test_circle_with_direction(reporter, SkPathDirection::kCCW);
 
     // multiple addCircle()
     SkPath path;
-    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
-    path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
+    path.addCircle(0, 0, SkIntToScalar(20), SkPathDirection::kCW);
     check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // some extra lineTo() would make isOval() fail
     path.reset();
-    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
     path.lineTo(0, 0);
     check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // not back to the original point
     path.reset();
-    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
     path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
     check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
@@ -3509,7 +3508,7 @@
 
     // test negative radius
     path.reset();
-    path.addCircle(0, 0, -1, SkPath::kCW_Direction);
+    path.addCircle(0, 0, -1, SkPathDirection::kCW);
     REPORTER_ASSERT(reporter, path.isEmpty());
 }
 
@@ -3518,7 +3517,7 @@
     SkMatrix m;
     SkPath path;
     unsigned start = 0;
-    SkPath::Direction dir = SkPath::kCCW_Direction;
+    SkPathDirection dir = SkPathDirection::kCCW;
 
     rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
     path.addOval(rect);
@@ -3533,7 +3532,7 @@
     // is unchanged.
     REPORTER_ASSERT(reporter, SkPathPriv::IsOval(tmp, nullptr, &dir, &start));
     REPORTER_ASSERT(reporter, 2 == start);
-    REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
+    REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
 
     m.reset();
     m.setRotate(SkIntToScalar(30));
@@ -3572,7 +3571,7 @@
     tmp.addOval(rect);
     path = tmp;
     REPORTER_ASSERT(reporter, SkPathPriv::IsOval(path, nullptr, &dir, &start));
-    REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
+    REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
     REPORTER_ASSERT(reporter, 1 == start);
 }
 
@@ -3584,27 +3583,27 @@
     REPORTER_ASSERT(reporter, 0 == p.countVerbs());
     REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
     REPORTER_ASSERT(reporter, p.isConvex());
-    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
+    REPORTER_ASSERT(reporter, p.getNewFillType() == SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, !p.isInverseFillType());
     REPORTER_ASSERT(reporter, p == empty);
     REPORTER_ASSERT(reporter, !(p != empty));
 }
 
 static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path,
-                                 SkPath::Direction dir) {
+                                 SkPathDirection dir) {
     REPORTER_ASSERT(reporter, path->isConvex());
     REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
-    path->setConvexity(SkPath::kUnknown_Convexity);
+    path->setConvexityType(SkPathConvexityType::kUnknown);
     REPORTER_ASSERT(reporter, path->isConvex());
     path->reset();
 }
 
 static void test_rrect_convexity_is_unknown(skiatest::Reporter* reporter, SkPath* path,
-                                 SkPath::Direction dir) {
+                                 SkPathDirection dir) {
     REPORTER_ASSERT(reporter, path->isConvex());
     REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
-    path->setConvexity(SkPath::kUnknown_Convexity);
-    REPORTER_ASSERT(reporter, path->getConvexity() == SkPath::kConcave_Convexity);
+    path->setConvexityType(SkPathConvexityType::kUnknown);
+    REPORTER_ASSERT(reporter, path->getConvexityType() == SkPathConvexityType::kConcave);
     path->reset();
 }
 
@@ -3615,45 +3614,45 @@
     SkRect r = {10, 20, 30, 40};
     rr.setRectRadii(r, radii);
     p.addRRect(rr);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
-    p.addRRect(rr, SkPath::kCCW_Direction);
-    test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
+    p.addRRect(rr, SkPathDirection::kCCW);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
     p.addRoundRect(r, &radii[0].fX);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
-    p.addRoundRect(r, &radii[0].fX, SkPath::kCCW_Direction);
-    test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
+    p.addRoundRect(r, &radii[0].fX, SkPathDirection::kCCW);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
     p.addRoundRect(r, radii[1].fX, radii[1].fY);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
-    p.addRoundRect(r, radii[1].fX, radii[1].fY, SkPath::kCCW_Direction);
-    test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
+    p.addRoundRect(r, radii[1].fX, radii[1].fY, SkPathDirection::kCCW);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
     for (size_t i = 0; i < SK_ARRAY_COUNT(radii); ++i) {
         SkVector save = radii[i];
         radii[i].set(0, 0);
         rr.setRectRadii(r, radii);
         p.addRRect(rr);
-        test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
+        test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
         radii[i] = save;
     }
     p.addRoundRect(r, 0, 0);
     SkRect returnedRect;
     REPORTER_ASSERT(reporter, p.isRect(&returnedRect));
     REPORTER_ASSERT(reporter, returnedRect == r);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
     SkVector zeroRadii[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
     rr.setRectRadii(r, zeroRadii);
     p.addRRect(rr);
     bool closed;
-    SkPath::Direction dir;
+    SkPathDirection dir;
     REPORTER_ASSERT(reporter, p.isRect(nullptr, &closed, &dir));
     REPORTER_ASSERT(reporter, closed);
-    REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
-    test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
-    p.addRRect(rr, SkPath::kCW_Direction);
-    p.addRRect(rr, SkPath::kCW_Direction);
+    REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
+    test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
+    p.addRRect(rr, SkPathDirection::kCW);
+    p.addRRect(rr, SkPathDirection::kCW);
     REPORTER_ASSERT(reporter, !p.isConvex());
     p.reset();
-    p.addRRect(rr, SkPath::kCCW_Direction);
-    p.addRRect(rr, SkPath::kCCW_Direction);
+    p.addRRect(rr, SkPathDirection::kCCW);
+    p.addRRect(rr, SkPathDirection::kCCW);
     REPORTER_ASSERT(reporter, !p.isConvex());
     p.reset();
     SkRect emptyR = {10, 20, 10, 30};
@@ -3666,7 +3665,7 @@
     SkRect largeR = {0, 0, SK_ScalarMax, SK_ScalarMax};
     rr.setRectRadii(largeR, radii);
     p.addRRect(rr);
-    test_rrect_convexity_is_unknown(reporter, &p, SkPath::kCW_Direction);
+    test_rrect_convexity_is_unknown(reporter, &p, SkPathDirection::kCW);
 
     // we check for non-finites
     SkRect infR = {0, 0, SK_ScalarMax, SK_ScalarInfinity};
@@ -3676,7 +3675,7 @@
     // We consider any path with very small (numerically unstable) edges to be concave.
     SkRect tinyR = {0, 0, 1e-9f, 1e-9f};
     p.addRoundRect(tinyR, 5e-11f, 5e-11f);
-    test_rrect_convexity_is_unknown(reporter, &p, SkPath::kCW_Direction);
+    test_rrect_convexity_is_unknown(reporter, &p, SkPathDirection::kCW);
 }
 
 static void test_arc(skiatest::Reporter* reporter) {
@@ -3696,7 +3695,7 @@
     REPORTER_ASSERT(reporter, p == cwOval);
     p.reset();
     SkPath ccwOval;
-    ccwOval.addOval(oval, SkPath::kCCW_Direction);
+    ccwOval.addOval(oval, SkPathDirection::kCCW);
     p.addArc(oval, 0, -360);
     REPORTER_ASSERT(reporter, p == ccwOval);
     p.reset();
@@ -3704,10 +3703,10 @@
     // diagonal colinear points make arc convex
     // TODO: one way to keep it concave would be to introduce interpolated on curve points
     // between control points and computing the on curve point at scan conversion time
-    REPORTER_ASSERT(reporter, p.getConvexity() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, p.getConvexityType() == SkPathConvexityType::kConvex);
     REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p, SkPathPriv::kCW_FirstDirection));
-    p.setConvexity(SkPath::kUnknown_Convexity);
-    REPORTER_ASSERT(reporter, p.getConvexity() == SkPath::kConvex_Convexity);
+    p.setConvexityType(SkPathConvexityType::kUnknown);
+    REPORTER_ASSERT(reporter, p.getConvexityType() == SkPathConvexityType::kConvex);
 }
 
 static inline SkScalar oval_start_index_to_angle(unsigned start) {
@@ -3738,7 +3737,7 @@
 static void check_oval_arc(skiatest::Reporter* reporter, SkScalar start, SkScalar sweep,
                            const SkPath& path) {
     SkRect r = SkRect::MakeEmpty();
-    SkPath::Direction d = SkPath::kCCW_Direction;
+    SkPathDirection d = SkPathDirection::kCCW;
     unsigned s = ~0U;
     bool isOval = SkPathPriv::IsOval(path, &r, &d, &s);
     REPORTER_ASSERT(reporter, isOval);
@@ -3746,7 +3745,7 @@
     recreatedPath.addOval(r, d, s);
     REPORTER_ASSERT(reporter, path == recreatedPath);
     REPORTER_ASSERT(reporter, oval_start_index_to_angle(s) == canonical_start_angle(start));
-    REPORTER_ASSERT(reporter, (SkPath::kCW_Direction == d) == (sweep > 0.f));
+    REPORTER_ASSERT(reporter, (SkPathDirection::kCW == d) == (sweep > 0.f));
 }
 
 static void test_arc_ovals(skiatest::Reporter* reporter) {
@@ -4027,9 +4026,9 @@
     (void) p.contains(-77.2027664f, 15.3066053f);
 
     p.reset();
-    p.setFillType(SkPath::kInverseWinding_FillType);
+    p.setFillType(SkPathFillType::kInverseWinding);
     REPORTER_ASSERT(reporter, p.contains(0, 0));
-    p.setFillType(SkPath::kWinding_FillType);
+    p.setFillType(SkPathFillType::kWinding);
     REPORTER_ASSERT(reporter, !p.contains(0, 0));
     p.moveTo(4, 4);
     p.lineTo(6, 8);
@@ -4154,7 +4153,7 @@
     SkPoint pts[] = {{5, 4}, {6, 5}, {7, 6}, {6, 6}, {4, 6}, {5, 7}, {5, 5}, {5, 4}, {6, 5}, {7, 6}};
     for (int i = 0; i < 3; ++i) {
         p.reset();
-        p.setFillType(SkPath::kEvenOdd_FillType);
+        p.setFillType(SkPathFillType::kEvenOdd);
         p.moveTo(pts[i].fX, pts[i].fY);
         p.cubicTo(pts[i + 1].fX, pts[i + 1].fY, pts[i + 2].fX, pts[i + 2].fY, pts[i + 3].fX, pts[i + 3].fY);
         p.cubicTo(pts[i + 4].fX, pts[i + 4].fY, pts[i + 5].fX, pts[i + 5].fY, pts[i + 6].fX, pts[i + 6].fY);
@@ -4252,7 +4251,7 @@
     SkPath b;
     REPORTER_ASSERT(reporter, a == a);
     REPORTER_ASSERT(reporter, a == b);
-    a.setFillType(SkPath::kInverseWinding_FillType);
+    a.setFillType(SkPathFillType::kInverseWinding);
     REPORTER_ASSERT(reporter, a != b);
     a.reset();
     REPORTER_ASSERT(reporter, a == b);
@@ -4283,51 +4282,51 @@
 
 static void test_dump(skiatest::Reporter* reporter) {
     SkPath p;
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n");
-    compare_dump(reporter, p, true, false,  "path.setFillType(SkPath::kWinding_FillType);\n");
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n");
+    compare_dump(reporter, p, true, false,  "path.setFillType(SkPathFillType::kWinding);\n");
     p.moveTo(1, 2);
     p.lineTo(3, 4);
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n");
-    compare_dump(reporter, p, true, false,  "path.setFillType(SkPath::kWinding_FillType);\n"
+    compare_dump(reporter, p, true, false,  "path.setFillType(SkPathFillType::kWinding);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n"
                                             "path.lineTo(1, 2);\n"
                                             "path.close();\n");
     p.reset();
-    p.setFillType(SkPath::kEvenOdd_FillType);
+    p.setFillType(SkPathFillType::kEvenOdd);
     p.moveTo(1, 2);
     p.quadTo(3, 4, 5, 6);
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kEvenOdd_FillType);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kEvenOdd);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.quadTo(3, 4, 5, 6);\n");
     p.reset();
-    p.setFillType(SkPath::kInverseWinding_FillType);
+    p.setFillType(SkPathFillType::kInverseWinding);
     p.moveTo(1, 2);
     p.conicTo(3, 4, 5, 6, 0.5f);
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseWinding_FillType);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kInverseWinding);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.conicTo(3, 4, 5, 6, 0.5f);\n");
     p.reset();
-    p.setFillType(SkPath::kInverseEvenOdd_FillType);
+    p.setFillType(SkPathFillType::kInverseEvenOdd);
     p.moveTo(1, 2);
     p.cubicTo(3, 4, 5, 6, 7, 8);
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kInverseEvenOdd_FillType);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kInverseEvenOdd);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.cubicTo(3, 4, 5, 6, 7, 8);\n");
     p.reset();
-    p.setFillType(SkPath::kWinding_FillType);
+    p.setFillType(SkPathFillType::kWinding);
     p.moveTo(1, 2);
     p.lineTo(3, 4);
     compare_dump(reporter, p, false, true,
-                 "path.setFillType(SkPath::kWinding_FillType);\n"
+                 "path.setFillType(SkPathFillType::kWinding);\n"
                  "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));  // 1, 2\n"
                  "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));  // 3, 4\n");
     p.reset();
     p.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));
     p.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));
-    compare_dump(reporter, p, false, false, "path.setFillType(SkPath::kWinding_FillType);\n"
+    compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n"
                                             "path.moveTo(1, 2);\n"
                                             "path.lineTo(3, 4);\n");
 }
@@ -4526,7 +4525,7 @@
 
 static void test_skbug_7015() {
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(SkBits2Float(0x4388c000), SkBits2Float(0x43947c08));  // 273.5f, 296.969f
     path.lineTo(SkBits2Float(0x4386c000), SkBits2Float(0x43947c08));  // 269.5f, 296.969f
     // 269.297f, 292.172f, 273.695f, 292.172f, 273.5f, 296.969f
@@ -4550,7 +4549,7 @@
 static void test_skbug_7435() {
     SkPaint paint;
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
+    path.setFillType(SkPathFillType::kWinding);
     path.moveTo(SkBits2Float(0x7f07a5af), SkBits2Float(0xff07ff1d));  // 1.80306e+38f, -1.8077e+38f
     path.lineTo(SkBits2Float(0x7edf4b2d), SkBits2Float(0xfedffe0a));  // 1.48404e+38f, -1.48868e+38f
     path.lineTo(SkBits2Float(0x7edf4585), SkBits2Float(0xfee003b2));  // 1.48389e+38f, -1.48883e+38f
@@ -5148,7 +5147,7 @@
 
     REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
     for (bool aa : {false, true}) {
-        for (SkPath::FillType ft : {SkPath::kWinding_FillType, SkPath::kInverseWinding_FillType}) {
+        for (auto ft : {SkPathFillType::kWinding, SkPathFillType::kInverseWinding}) {
             for (SkScalar bad : {SK_ScalarInfinity, SK_ScalarNaN}) {
                 for (int bits = 1; bits <= 15; ++bits) {
                     SkPoint p0 = { 0, 0 };
@@ -5488,8 +5487,8 @@
 };
 
 static bool conditional_convex(const SkPath& path, bool is_convex) {
-    SkPath::Convexity c = path.getConvexityOrUnknown();
-    return is_convex ? (c == SkPath::kConvex_Convexity) : (c != SkPath::kConvex_Convexity);
+    SkPathConvexityType c = path.getConvexityTypeOrUnknown();
+    return is_convex ? (c == SkPathConvexityType::kConvex) : (c != SkPathConvexityType::kConvex);
 }
 
 // expect axis-aligned shape to survive assignment, identity and scale/translate matrices
@@ -5498,22 +5497,22 @@
              ISA isa_proc) {
     REPORTER_ASSERT(reporter, isa_proc(*path));
     // force the issue (computing convexity) the first time.
-    REPORTER_ASSERT(reporter, path->getConvexity() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, path->getConvexityType() == SkPathConvexityType::kConvex);
 
     SkPath path2;
 
     // a path's isa and convexity should survive assignment
     path2 = *path;
     REPORTER_ASSERT(reporter, isa_proc(path2));
-    REPORTER_ASSERT(reporter, path2.getConvexityOrUnknown() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, path2.getConvexityTypeOrUnknown() == SkPathConvexityType::kConvex);
 
     // a path's isa and convexity should identity transform
     path->transform(x.fIM, &path2);
     path->transform(x.fIM);
     REPORTER_ASSERT(reporter, isa_proc(path2));
-    REPORTER_ASSERT(reporter, path2.getConvexityOrUnknown() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, path2.getConvexityTypeOrUnknown() == SkPathConvexityType::kConvex);
     REPORTER_ASSERT(reporter, isa_proc(*path));
-    REPORTER_ASSERT(reporter, path->getConvexityOrUnknown() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, path->getConvexityTypeOrUnknown() == SkPathConvexityType::kConvex);
 
     // a path's isa should survive translation, convexity depends on axis alignment
     path->transform(x.fTM, &path2);
@@ -5533,15 +5532,15 @@
 
     // For security, post-rotation, we can't assume we're still convex. It might prove to be,
     // in fact, still be convex, be we can't have cached that setting, hence the call to
-    // getConvexityOrUnknown() instead of getConvexity().
+    // getConvexityTypeOrUnknown() instead of getConvexityType().
     path->transform(x.fRM, &path2);
     path->transform(x.fRM);
     if (isAxisAligned) {
         REPORTER_ASSERT(reporter, !isa_proc(path2));
         REPORTER_ASSERT(reporter, !isa_proc(*path));
     }
-    REPORTER_ASSERT(reporter, path2.getConvexityOrUnknown() != SkPath::kConvex_Convexity);
-    REPORTER_ASSERT(reporter, path->getConvexityOrUnknown() != SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(reporter, path2.getConvexityTypeOrUnknown() != SkPathConvexityType::kConvex);
+    REPORTER_ASSERT(reporter, path->getConvexityTypeOrUnknown() != SkPathConvexityType::kConvex);
 }
 
 DEF_TEST(Path_survive_transform, r) {
@@ -5562,7 +5561,7 @@
     // make a trapazoid; definitely convex, but not marked as axis-aligned (e.g. oval, rrect)
     path.reset();
     path.moveTo(0, 0).lineTo(100, 0).lineTo(70, 100).lineTo(30, 100);
-    REPORTER_ASSERT(r, path.getConvexity() == SkPath::kConvex_Convexity);
+    REPORTER_ASSERT(r, path.getConvexityType() == SkPathConvexityType::kConvex);
     survive(&path, x, false, r, [](const SkPath& p) { return true; });
 }
 
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 27243c2..a73cd9e 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -379,7 +379,7 @@
 
     SkPath invPath;
     invPath.addOval(rect1);
-    invPath.setFillType(SkPath::kInverseEvenOdd_FillType);
+    invPath.setFillType(SkPathFillType::kInverseEvenOdd);
     SkPath path;
     path.addOval(rect2);
     SkPath path2;
diff --git a/tests/PrimitiveProcessorTest.cpp b/tests/PrimitiveProcessorTest.cpp
index 02fc02b..e9a10e0 100644
--- a/tests/PrimitiveProcessorTest.cpp
+++ b/tests/PrimitiveProcessorTest.cpp
@@ -57,6 +57,38 @@
     void onPrepareDraws(Target* target) override {
         class GP : public GrGeometryProcessor {
         public:
+            static GrGeometryProcessor* Make(SkArenaAlloc* arena, int numAttribs) {
+                return arena->make<GP>(numAttribs);
+            }
+
+            const char* name() const override { return "Dummy GP"; }
+
+            GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
+                class GLSLGP : public GrGLSLGeometryProcessor {
+                public:
+                    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+                        const GP& gp = args.fGP.cast<GP>();
+                        args.fVaryingHandler->emitAttributes(gp);
+                        this->writeOutputPosition(args.fVertBuilder, gpArgs,
+                                                  gp.fAttributes[0].name());
+                        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+                        fragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
+                        fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
+                    }
+                    void setData(const GrGLSLProgramDataManager& pdman,
+                                 const GrPrimitiveProcessor& primProc,
+                                 const CoordTransformRange&) override {}
+                };
+                return new GLSLGP();
+            }
+            void getGLSLProcessorKey(const GrShaderCaps&,
+                                     GrProcessorKeyBuilder* builder) const override {
+                builder->add32(fNumAttribs);
+            }
+
+        private:
+            friend class ::SkArenaAlloc; // for access to ctor
+
             GP(int numAttribs) : INHERITED(kGP_ClassID), fNumAttribs(numAttribs) {
                 SkASSERT(numAttribs > 1);
                 fAttribNames.reset(new SkString[numAttribs]);
@@ -75,44 +107,20 @@
                 }
                 this->setVertexAttributes(fAttributes.get(), numAttribs);
             }
-            const char* name() const override { return "Dummy GP"; }
 
-            GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
-                class GLSLGP : public GrGLSLGeometryProcessor {
-                public:
-                    void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
-                        const GP& gp = args.fGP.cast<GP>();
-                        args.fVaryingHandler->emitAttributes(gp);
-                        this->writeOutputPosition(args.fVertBuilder, gpArgs,
-                                                  gp.fAttributes[0].name());
-                        GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
-                        fragBuilder->codeAppendf("%s = half4(1);", args.fOutputColor);
-                        fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
-                    }
-                    void setData(const GrGLSLProgramDataManager& pdman,
-                                 const GrPrimitiveProcessor& primProc,
-                                 FPCoordTransformIter&&) override {}
-                };
-                return new GLSLGP();
-            }
-            void getGLSLProcessorKey(const GrShaderCaps&,
-                                     GrProcessorKeyBuilder* builder) const override {
-                builder->add32(fNumAttribs);
-            }
-
-        private:
             int fNumAttribs;
             std::unique_ptr<SkString[]> fAttribNames;
             std::unique_ptr<Attribute[]> fAttributes;
 
             typedef GrGeometryProcessor INHERITED;
         };
-        sk_sp<GrGeometryProcessor> gp(new GP(fNumAttribs));
+
+        GrGeometryProcessor* gp = GP::Make(target->allocator(), fNumAttribs);
         size_t vertexStride = gp->vertexStride();
         QuadHelper helper(target, vertexStride, 1);
         SkPoint* vertices = reinterpret_cast<SkPoint*>(helper.vertices());
         SkPointPriv::SetRectTriStrip(vertices, 0.f, 0.f, 1.f, 1.f, vertexStride);
-        helper.recordDraw(target, std::move(gp));
+        helper.recordDraw(target, gp);
     }
 
     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index 7171868..978f7ef 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -231,22 +231,22 @@
                   GrRenderTargetContext* rtc,
                   std::unique_ptr<GrFragmentProcessor> fp,
                   sk_sp<GrTextureProxy> inputDataProxy,
-                  GrColorType inputColorType) {
+                  SkAlphaType inputAlphaType) {
     GrPaint paint;
-    paint.addColorTextureProcessor(std::move(inputDataProxy), inputColorType, SkMatrix::I());
+    paint.addColorTextureProcessor(std::move(inputDataProxy), inputAlphaType, SkMatrix::I());
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
     auto op = GrFillRectOp::MakeNonAARect(context, std::move(paint), SkMatrix::I(),
                                           SkRect::MakeWH(rtc->width(), rtc->height()));
-    rtc->addDrawOp(GrNoClip(), std::move(op));
+    rtc->priv().testingOnly_addDrawOp(std::move(op));
 }
 
 // This assumes that the output buffer will be the same size as inputDataProxy
 void render_fp(GrContext* context, GrRenderTargetContext* rtc, GrFragmentProcessor* fp,
-               sk_sp<GrTextureProxy> inputDataProxy, GrColorType inputColorType, GrColor* buffer) {
+               sk_sp<GrTextureProxy> inputDataProxy, SkAlphaType inputAlphaType, GrColor* buffer) {
     // test_draw_op needs to take ownership of an FP, so give it a clone that it can own
-    test_draw_op(context, rtc, fp->clone(), inputDataProxy, inputColorType);
+    test_draw_op(context, rtc, fp->clone(), inputDataProxy, inputAlphaType);
     memset(buffer, 0x0, sizeof(GrColor) * inputDataProxy->width() * inputDataProxy->height());
     rtc->readPixels(SkImageInfo::Make(inputDataProxy->dimensions(), kRGBA_8888_SkColorType,
                                       kPremul_SkAlphaType),
@@ -257,8 +257,7 @@
 bool init_test_textures(GrResourceProvider* resourceProvider,
                         GrProxyProvider* proxyProvider,
                         SkRandom* random,
-                        sk_sp<GrTextureProxy> proxies[2],
-                        GrColorType proxyColorTypes[2]) {
+                        sk_sp<GrTextureProxy> proxies[2]) {
     static const int kTestTextureSize = 256;
 
     {
@@ -278,7 +277,6 @@
         proxies[0] =
                 proxyProvider->createTextureProxy(img, 1, SkBudgeted::kYes, SkBackingFit::kExact);
         proxies[0]->instantiate(resourceProvider);
-        proxyColorTypes[0] = GrColorType::kRGBA_8888;
     }
 
     {
@@ -297,7 +295,6 @@
         proxies[1] =
                 proxyProvider->createTextureProxy(img, 1, SkBudgeted::kYes, SkBackingFit::kExact);
         proxies[1]->instantiate(resourceProvider);
-        proxyColorTypes[1] = GrColorType::kAlpha_8;
     }
 
     return proxies[0] && proxies[1];
@@ -431,12 +428,11 @@
             SkBackingFit::kExact, kRenderSize, kRenderSize, GrColorType::kRGBA_8888, nullptr);
 
     sk_sp<GrTextureProxy> proxies[2];
-    GrColorType proxyColorTypes[2];
-    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies, proxyColorTypes)) {
+    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies)) {
         ERRORF(reporter, "Could not create test textures");
         return;
     }
-    GrProcessorTestData testData(&random, context, rtc.get(), proxies, proxyColorTypes);
+    GrProcessorTestData testData(&random, context, rtc.get(), proxies);
 
     // Coverage optimization uses three frames with a linearly transformed input texture.  The first
     // frame has no offset, second frames add .2 and .4, which should then be present as a fixed
@@ -483,14 +479,14 @@
 
             if (fp->compatibleWithCoverageAsAlpha()) {
                 // 2nd and 3rd frames are only used when checking coverage optimization
-                render_fp(context, rtc.get(), fp.get(), inputTexture2, GrColorType::kRGBA_8888,
+                render_fp(context, rtc.get(), fp.get(), inputTexture2, kPremul_SkAlphaType,
                           readData2.get());
-                render_fp(context, rtc.get(), fp.get(), inputTexture3, GrColorType::kRGBA_8888,
+                render_fp(context, rtc.get(), fp.get(), inputTexture3, kPremul_SkAlphaType,
                           readData3.get());
             }
             // Draw base frame last so that rtc holds the original FP behavior if we need to
             // dump the image to the log.
-            render_fp(context, rtc.get(), fp.get(), inputTexture1, GrColorType::kRGBA_8888,
+            render_fp(context, rtc.get(), fp.get(), inputTexture1, kPremul_SkAlphaType,
                       readData1.get());
 
             if (0) {  // Useful to see what FPs are being tested.
@@ -669,12 +665,11 @@
             SkBackingFit::kExact, kRenderSize, kRenderSize, GrColorType::kRGBA_8888, nullptr);
 
     sk_sp<GrTextureProxy> proxies[2];
-    GrColorType proxyColorTypes[2];
-    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies, proxyColorTypes)) {
+    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies)) {
         ERRORF(reporter, "Could not create test textures");
         return;
     }
-    GrProcessorTestData testData(&random, context, rtc.get(), proxies, proxyColorTypes);
+    GrProcessorTestData testData(&random, context, rtc.get(), proxies);
 
     auto inputTexture = make_input_texture(proxyProvider, kRenderSize, kRenderSize, 0.0f);
     std::unique_ptr<GrColor[]> readData1(new GrColor[kRenderSize * kRenderSize]);
@@ -714,11 +709,11 @@
             REPORTER_ASSERT(reporter, fp->numChildProcessors() == clone->numChildProcessors());
             REPORTER_ASSERT(reporter, fp->usesLocalCoords() == clone->usesLocalCoords());
             // Draw with original and read back the results.
-            render_fp(context, rtc.get(), fp.get(), inputTexture, GrColorType::kRGBA_8888,
+            render_fp(context, rtc.get(), fp.get(), inputTexture, kPremul_SkAlphaType,
                       readData1.get());
 
             // Draw with clone and read back the results.
-            render_fp(context, rtc.get(), clone.get(), inputTexture, GrColorType::kRGBA_8888,
+            render_fp(context, rtc.get(), clone.get(), inputTexture, kPremul_SkAlphaType,
                       readData2.get());
 
             // Check that the results are the same.
diff --git a/tests/ProgramsTest.cpp b/tests/ProgramsTest.cpp
index 5225009..d12ba0d 100644
--- a/tests/ProgramsTest.cpp
+++ b/tests/ProgramsTest.cpp
@@ -261,7 +261,6 @@
     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
 
     sk_sp<GrTextureProxy> proxies[2];
-    GrColorType proxyColorTypes[2];
 
     // setup dummy textures
     GrMipMapped mipMapped = GrMipMapped(context->priv().caps()->mipMapSupport());
@@ -277,7 +276,6 @@
                                                 kBottomLeft_GrSurfaceOrigin, mipMapped,
                                                 SkBackingFit::kExact, SkBudgeted::kNo,
                                                 GrProtected::kNo, GrInternalSurfaceFlags::kNone);
-        proxyColorTypes[0] = GrColorType::kRGBA_8888;
     }
     {
         GrSurfaceDesc dummyDesc;
@@ -291,7 +289,6 @@
                                                 kTopLeft_GrSurfaceOrigin, mipMapped,
                                                 SkBackingFit::kExact, SkBudgeted::kNo,
                                                 GrProtected::kNo, GrInternalSurfaceFlags::kNone);
-        proxyColorTypes[1] = GrColorType::kAlpha_8;
     }
 
     if (!proxies[0] || !proxies[1]) {
@@ -299,9 +296,6 @@
         return false;
     }
 
-    // dummy scissor state
-    GrScissorState scissor;
-
     SkRandom random;
     static const int NUM_TESTS = 1024;
     for (int t = 0; t < NUM_TESTS; t++) {
@@ -314,8 +308,7 @@
         }
 
         GrPaint paint;
-        GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies,
-                                proxyColorTypes);
+        GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
         set_random_color_coverage_stages(&paint, &ptd, maxStages, maxLevels);
         set_random_xpf(&paint, &ptd);
         GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint));
@@ -340,8 +333,7 @@
     for (int i = 0; i < fpFactoryCnt; ++i) {
         // Since FP factories internally randomize, call each 10 times.
         for (int j = 0; j < 10; ++j) {
-            GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies,
-                                    proxyColorTypes);
+            GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies);
 
             GrPaint paint;
             paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
diff --git a/tests/ProxyTest.cpp b/tests/ProxyTest.cpp
index 28eb8d6..5a83ccd 100644
--- a/tests/ProxyTest.cpp
+++ b/tests/ProxyTest.cpp
@@ -20,8 +20,10 @@
 #include "src/gpu/GrSurfaceProxyPriv.h"
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/SkGr.h"
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLDefines.h"
 #include "src/gpu/gl/GrGLUtil.h"
+#endif
 
 // Check that the surface proxy's member vars are set as expected
 static void check_surface(skiatest::Reporter* reporter,
@@ -258,6 +260,7 @@
                     continue;
                 }
 
+#ifdef SK_GL
                 // Test wrapping FBO 0 (with made up properties). This tests sample count and the
                 // special case where FBO 0 doesn't support window rectangles.
                 if (GrBackendApi::kOpenGL == ctxInfo.backend()) {
@@ -278,6 +281,7 @@
                                        sProxy->asRenderTargetProxy(),
                                        supportedNumSamples, SkBackingFit::kExact, 0);
                 }
+#endif
 
                 // Tests wrapBackendRenderTarget with a GrBackendTexture
                 {
diff --git a/tests/RRectInPathTest.cpp b/tests/RRectInPathTest.cpp
index a25ad45..c36a5ba 100644
--- a/tests/RRectInPathTest.cpp
+++ b/tests/RRectInPathTest.cpp
@@ -12,7 +12,7 @@
 #include "tests/Test.h"
 
 static SkRRect path_contains_rrect(skiatest::Reporter* reporter, const SkPath& path,
-                                   SkPath::Direction* dir, unsigned* start) {
+                                   SkPathDirection* dir, unsigned* start) {
     SkRRect out;
     REPORTER_ASSERT(reporter, SkPathPriv::IsRRect(path, &out, dir, start));
     SkPath recreatedPath;
@@ -30,7 +30,7 @@
         SkPath xformed;
         path.transform(m, &xformed);
         SkRRect xrr = SkRRect::MakeRect(SkRect::MakeEmpty());
-        SkPath::Direction xd = SkPath::kCCW_Direction;
+        SkPathDirection xd = SkPathDirection::kCCW;
         unsigned xs = ~0U;
         REPORTER_ASSERT(reporter, SkPathPriv::IsRRect(xformed, &xrr, &xd, &xs));
         recreatedPath.reset();
@@ -41,7 +41,7 @@
 }
 
 static SkRRect inner_path_contains_rrect(skiatest::Reporter* reporter, const SkRRect& in,
-                                         SkPath::Direction dir, unsigned start) {
+                                         SkPathDirection dir, unsigned start) {
     switch (in.getType()) {
         case SkRRect::kEmpty_Type:
         case SkRRect::kRect_Type:
@@ -52,7 +52,7 @@
     }
     SkPath path;
     path.addRRect(in, dir, start);
-    SkPath::Direction outDir;
+    SkPathDirection outDir;
     unsigned outStart;
     SkRRect rrect = path_contains_rrect(reporter, path, &outDir, &outStart);
     REPORTER_ASSERT(reporter, outDir == dir && outStart == start);
@@ -60,7 +60,7 @@
 }
 
 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRRect& in,
-                                      SkPath::Direction dir, unsigned start) {
+                                      SkPathDirection dir, unsigned start) {
     SkRRect out = inner_path_contains_rrect(reporter, in, dir, start);
     if (in != out) {
         SkDebugf("");
@@ -69,7 +69,7 @@
 }
 
 static void path_contains_rrect_nocheck(skiatest::Reporter* reporter, const SkRRect& in,
-                                        SkPath::Direction dir, unsigned start) {
+                                        SkPathDirection dir, unsigned start) {
     SkRRect out = inner_path_contains_rrect(reporter, in, dir, start);
     if (in == out) {
         SkDebugf("");
@@ -77,7 +77,7 @@
 }
 
 static void path_contains_rrect_check(skiatest::Reporter* reporter, const SkRect& r,
-        SkVector v[4], SkPath::Direction dir, unsigned start) {
+        SkVector v[4], SkPathDirection dir, unsigned start) {
     SkRRect rrect;
     rrect.setRectRadii(r, v);
     path_contains_rrect_check(reporter, rrect, dir, start);
@@ -85,15 +85,15 @@
 
 class ForceIsRRect_Private {
 public:
-    ForceIsRRect_Private(SkPath* path, SkPath::Direction dir, unsigned start) {
-        path->fPathRef->setIsRRect(true, dir == SkPath::kCCW_Direction, start);
+    ForceIsRRect_Private(SkPath* path, SkPathDirection dir, unsigned start) {
+        path->fPathRef->setIsRRect(true, dir == SkPathDirection::kCCW, start);
     }
 };
 
 static void force_path_contains_rrect(skiatest::Reporter* reporter, SkPath& path,
-                                      SkPath::Direction dir, unsigned start) {
+                                      SkPathDirection dir, unsigned start) {
     ForceIsRRect_Private force_rrect(&path, dir, start);
-    SkPath::Direction outDir;
+    SkPathDirection outDir;
     unsigned outStart;
     path_contains_rrect(reporter, path, &outDir, &outStart);
     REPORTER_ASSERT(reporter, outDir == dir && outStart == start);
@@ -124,7 +124,7 @@
     path.lineTo(3.5f, 66);
     path.conicTo(0, 66, 0, 62.5, weight);
     path.close();
-    force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6);
+    force_path_contains_rrect(reporter, path, SkPathDirection::kCW, 6);
 
     path.reset();
     path.moveTo(0, 81.5f);
@@ -137,7 +137,7 @@
     path.lineTo(3.5f, 85);
     path.conicTo(0, 85, 0, 81.5f, weight);
     path.close();
-    force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6);
+    force_path_contains_rrect(reporter, path, SkPathDirection::kCW, 6);
 
     path.reset();
     path.moveTo(14, 1189);
@@ -150,7 +150,7 @@
     path.lineTo(21, 1196);
     path.conicTo(14, 1196, 14, 1189, weight);
     path.close();
-    force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6);
+    force_path_contains_rrect(reporter, path, SkPathDirection::kCW, 6);
 
     path.reset();
     path.moveTo(14, 1743);
@@ -163,14 +163,14 @@
     path.lineTo(21, 1750);
     path.conicTo(14, 1750, 14, 1743, weight);
     path.close();
-    force_path_contains_rrect(reporter, path, SkPath::kCW_Direction, 6);
+    force_path_contains_rrect(reporter, path, SkPathDirection::kCW, 6);
 }
 
 static const SkScalar kWidth = 100.0f;
 static const SkScalar kHeight = 100.0f;
 
 static void test_tricky_radii(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             {
                 // crbug.com/458522
@@ -197,7 +197,7 @@
 }
 
 static void test_empty_crbug_458524(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             SkRRect rr;
             const SkRect bounds = { 3709, 3709, 3709 + 7402, 3709 + 29825 };
@@ -215,7 +215,7 @@
 }
 
 static void test_inset(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             SkRRect rr, rr2;
             SkRect r = { 0, 0, 100, 100 };
@@ -244,7 +244,7 @@
                               const SkRect& rect,
                               SkScalar l, SkScalar t, SkScalar r, SkScalar b,
                               bool checkRadii) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             SkRRect rr;
             rr.setNinePatch(rect, l, t, r, b);
@@ -268,7 +268,7 @@
 
 // Test out the basic API entry points
 static void test_round_rect_basic(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             //----
             SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
@@ -342,7 +342,7 @@
 
 // Test out the cases when the RR degenerates to a rect
 static void test_round_rect_rects(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             //----
             SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
@@ -371,7 +371,7 @@
 
 // Test out the cases when the RR degenerates to an oval
 static void test_round_rect_ovals(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             //----
             SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
@@ -385,7 +385,7 @@
 
 // Test out the non-degenerate RR cases
 static void test_round_rect_general(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             //----
             SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
@@ -406,7 +406,7 @@
 }
 
 static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
             SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
@@ -432,7 +432,7 @@
     const SkRect rectx = SkRect::MakeLTRB(min, min, max, big);
     const SkRect recty = SkRect::MakeLTRB(min, min, big, max);
 
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             SkVector radii[4];
             for (int i = 0; i < 4; ++i) {
@@ -445,7 +445,7 @@
 }
 
 static void test_mix(skiatest::Reporter* reporter) {
-    for (auto dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
+    for (auto dir : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
         for (int start = 0; start < 8; ++start) {
             // Test out mixed degenerate and non-degenerate geometry with Conics
             const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } };
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index cc678a4..00e3dd9 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -690,8 +690,8 @@
         } else if (!rules.fAllowUnpremulRead && readAT == kUnpremul_SkAlphaType) {
             REPORTER_ASSERT(reporter, !success);
         } else if (!success) {
-            // TODO: Support reading to kGray, support kRGB_101010x at all in GPU.
-            if (readCT != kGray_8_SkColorType && readCT != kRGB_101010x_SkColorType) {
+            // TODO: Support kRGB_101010x at all in GPU.
+            if (readCT != kRGB_101010x_SkColorType) {
                 ERRORF(reporter,
                        "Read failed. Src CT: %s, Src AT: %s Read CT: %s, Read AT: %s, "
                        "Rect [%d, %d, %d, %d], CS conversion: %d\n",
@@ -711,11 +711,15 @@
         if (success && srcReadRect.intersect(surfBounds, rect)) {
             SkIRect dstWriteRect = srcReadRect.makeOffset(-rect.fLeft, -rect.fTop);
 
-            // A CS conversion allows a 3 value difference and otherwise a 2 value difference. Note
-            // that sometimes read back on GPU can be lossy even when there no conversion at all
-            // because GPU->CPU read may go to a lower bit depth format and then be promoted back to
-            // the original type. For example, GL ES cannot read to 1010102, so we go through 8888.
-            float numer = (csConversion ? 3.f : 2.f);
+            const bool lumConversion =
+                    !(SkColorTypeComponentFlags(srcCT)  & kGray_SkColorTypeComponentFlag) &&
+                     (SkColorTypeComponentFlags(readCT) & kGray_SkColorTypeComponentFlag);
+            // A CS or luminance conversion allows a 3 value difference and otherwise a 2 value
+            // difference. Note that sometimes read back on GPU can be lossy even when there no
+            // conversion at allbecause GPU->CPU read may go to a lower bit depth format and then be
+            // promoted back to the original type. For example, GL ES cannot read to 1010102, so we
+            // go through 8888.
+            const float numer = (lumConversion || csConversion) ? 3.f : 2.f;
             int rgbBits = std::min({min_rgb_channel_bits(readCT),
                                     min_rgb_channel_bits(srcCT),
                                     8});
@@ -967,60 +971,6 @@
     }
 }
 
-DEF_GPUTEST_FOR_RENDERING_CONTEXTS(LegacyAsyncReadPixels, reporter, ctxInfo) {
-    using Surface = sk_sp<SkSurface>;
-    auto reader = std::function<GpuReadSrcFn<Surface>>([](const Surface& surface,
-                                                          const SkIVector& offset,
-                                                          const SkPixmap& pixels) {
-        struct Context {
-            const SkPixmap* fPixmap = nullptr;
-            bool fSuceeded = false;
-            bool fCalled = false;
-        };
-        auto callback = [](SkSurface::ReadPixelsContext c, const void* data, size_t rowBytes) {
-            auto* context = static_cast<Context*>(c);
-            context->fCalled = true;
-            if ((context->fSuceeded = SkToBool(data))) {
-                auto* pm = context->fPixmap;
-                SkRectMemcpy(pm->writable_addr(), pm->rowBytes(), data, rowBytes,
-                             pm->info().minRowBytes(), pm->height());
-            }
-        };
-
-        Context context;
-        context.fPixmap = &pixels;
-        auto rect = SkIRect::MakeSize(pixels.dimensions()).makeOffset(offset);
-
-        // Rescale quality and linearity don't matter since we're doing a non-scaling readback.
-        surface->asyncRescaleAndReadPixels(pixels.info(), rect, SkSurface::RescaleGamma::kSrc,
-                                           kNone_SkFilterQuality, callback, &context);
-        while (!context.fCalled) {
-            surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
-        }
-        return context.fSuceeded;
-    });
-    GpuReadPixelTestRules rules;
-    rules.fAllowUnpremulSrc = false;
-    rules.fAllowUnpremulRead = false;
-    rules.fUncontainedRectSucceeds = false;
-
-    for (GrSurfaceOrigin origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
-        auto factory = std::function<GpuSrcFactory<Surface>>(
-                [context = ctxInfo.grContext(), origin](const SkPixmap& src) {
-                    if (src.colorType() == kRGB_888x_SkColorType) {
-                        return Surface();
-                    }
-                    auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, src.info(),
-                                                            0, origin, nullptr);
-                    if (surf) {
-                        surf->writePixels(src, 0, 0);
-                    }
-                    return surf;
-                });
-        gpu_read_pixels_test_driver(reporter, rules, factory, reader);
-    }
-}
-
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadPixels_Gpu, reporter, ctxInfo) {
     using Surface = sk_sp<SkSurface>;
     auto reader = std::function<GpuReadSrcFn<Surface>>(
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
index 0aebc9e..b0a0834 100644
--- a/tests/Reader32Test.cpp
+++ b/tests/Reader32Test.cpp
@@ -81,7 +81,7 @@
 
     // need to handle read(null, 0) and not get undefined behavior from memcpy
     {
-        char storage[100];
+        uint32_t storage[100 / sizeof(uint32_t)];
         reader.setMemory(storage, sizeof(storage));
         char buffer[10];
         reader.read(buffer, 0);     // easy case, since we pass a ptr
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
index f1f9102..547ba24 100644
--- a/tests/RectangleTextureTest.cpp
+++ b/tests/RectangleTextureTest.cpp
@@ -17,15 +17,19 @@
 #include "src/gpu/GrSurfacePriv.h"
 #include "src/gpu/GrTexturePriv.h"
 #include "src/gpu/SkGr.h"
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLGpu.h"
 #include "src/gpu/gl/GrGLUtil.h"
+#endif
 #include "tools/gpu/ProxyUtils.h"
+#ifdef SK_GL
 #include "tools/gpu/gl/GLTestContext.h"
+#endif
 
 // skbug.com/5932
 static void test_basic_draw_as_src(skiatest::Reporter* reporter, GrContext* context,
                                    sk_sp<GrTextureProxy> rectProxy, GrColorType colorType,
-                                   uint32_t expectedPixelValues[]) {
+                                   SkAlphaType alphaType, uint32_t expectedPixelValues[]) {
     auto rtContext = context->priv().makeDeferredRenderTargetContext(
             SkBackingFit::kExact, rectProxy->width(), rectProxy->height(), colorType, nullptr);
     for (auto filter : {GrSamplerState::Filter::kNearest,
@@ -33,7 +37,7 @@
                         GrSamplerState::Filter::kMipMap}) {
         rtContext->clear(nullptr, SkPMColor4f::FromBytes_RGBA(0xDDCCBBAA),
                          GrRenderTargetContext::CanClearFullscreen::kYes);
-        auto fp = GrSimpleTextureEffect::Make(rectProxy, colorType, SkMatrix::I(), filter);
+        auto fp = GrSimpleTextureEffect::Make(rectProxy, alphaType, SkMatrix::I(), filter);
         GrPaint paint;
         paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
         paint.addColorFragmentProcessor(std::move(fp));
@@ -117,6 +121,7 @@
     }
 }
 
+#ifdef SK_GL
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(RectangleTexture, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
@@ -180,7 +185,8 @@
         SkASSERT(rectProxy->hasRestrictedSampling());
         SkASSERT(rectProxy->peekTexture()->texturePriv().hasRestrictedSampling());
 
-        test_basic_draw_as_src(reporter, context, rectProxy, GrColorType::kRGBA_8888, refPixels);
+        test_basic_draw_as_src(reporter, context, rectProxy, GrColorType::kRGBA_8888,
+                               kPremul_SkAlphaType, refPixels);
 
         // Test copy to both a texture and RT
         TestCopyFromSurface(reporter, context, rectProxy.get(), GrColorType::kRGBA_8888, refPixels,
@@ -201,3 +207,4 @@
         GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
     }
 }
+#endif
diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp
index 239ffaa..a7d585d 100644
--- a/tests/RegionTest.cpp
+++ b/tests/RegionTest.cpp
@@ -421,7 +421,7 @@
 
 DEF_TEST(region_inverse_union_skbug_7491, reporter) {
     SkPath path;
-    path.setFillType(SkPath::kInverseWinding_FillType);
+    path.setFillType(SkPathFillType::kInverseWinding);
     path.moveTo(10, 20); path.lineTo(10, 30); path.lineTo(10.1f, 10); path.close();
 
     SkRegion clip;
diff --git a/tests/ShaperTest.cpp b/tests/ShaperTest.cpp
index 64f09fb..3712ec0 100644
--- a/tests/ShaperTest.cpp
+++ b/tests/ShaperTest.cpp
@@ -59,24 +59,17 @@
     }
     void commitLine() override {}
 };
-}  // namespace
 
-static void cluster_test(skiatest::Reporter* reporter, const char* resource) {
+void shaper_test(skiatest::Reporter* reporter, const char* name, SkData* data) {
     auto shaper = SkShaper::Make();
     if (!shaper) {
         ERRORF(reporter, "Could not create shaper.");
         return;
     }
 
-    auto data = GetResourceAsData(resource);
-    if (!data) {
-        ERRORF(reporter, "Could not get resource %s.", resource);
-        return;
-    }
-
     constexpr float kWidth = 400;
     SkFont font(SkTypeface::MakeDefault());
-    RunHandler rh(resource, reporter);
+    RunHandler rh(name, reporter);
     shaper->shape((const char*)data->data(), data->size(), font, true, kWidth, &rh);
 
     constexpr SkFourByteTag latn = SkSetFourByteTag('l','a','t','n');
@@ -88,6 +81,20 @@
                   fontIterator, bidiIterator, scriptIterator, languageIterator, kWidth, &rh);
 }
 
+void cluster_test(skiatest::Reporter* reporter, const char* resource) {
+    auto data = GetResourceAsData(resource);
+    if (!data) {
+        ERRORF(reporter, "Could not get resource %s.", resource);
+        return;
+    }
+
+    shaper_test(reporter, resource, data.get());
+}
+
+}  // namespace
+
+DEF_TEST(Shaper_cluster_empty, r) { shaper_test(r, "empty", SkData::MakeEmpty().get()); }
+
 #define SHAPER_TEST(X) DEF_TEST(Shaper_cluster_ ## X, r) { cluster_test(r, "text/" #X ".txt"); }
 SHAPER_TEST(arabic)
 SHAPER_TEST(armenian)
diff --git a/tests/SkGlyphBufferTest.cpp b/tests/SkGlyphBufferTest.cpp
index 6840396..15b4df7 100644
--- a/tests/SkGlyphBufferTest.cpp
+++ b/tests/SkGlyphBufferTest.cpp
@@ -11,6 +11,89 @@
 #include "src/core/SkScalerContext.h"
 #include "tests/Test.h"
 
+DEF_TEST(SkPackedGlyphIDCtor, reporter) {
+    using PG = SkPackedGlyphID;
+    // x and y are in one quarter the sub-pixel sampling frequency.
+    // Number of steps on the interval [0, 1)
+    const int perUnit = 1u << (PG::kSubPixelPosLen + 2);
+    const float step = 1.f / perUnit;
+    const int testLimit = 2 * perUnit;
+    auto freqRound = [](uint32_t x) -> uint32_t {
+        return ((x + 2) >> 2) & PG::kSubPixelPosMask;
+    };
+
+    {
+        // Normal sub-pixel with y-axis snapping.
+        auto roundingSpec = SkGlyphPositionRoundingSpec(true, kX_SkAxisAlignment);
+        SkIPoint mask = roundingSpec.ignorePositionFieldMask;
+        for (int x = -testLimit; x < testLimit; x++) {
+            float fx = x * step;
+            SkPoint roundedPos = SkPoint{fx, 0} + roundingSpec.halfAxisSampleFreq;
+            SkPackedGlyphID packedID{3, roundedPos, mask};
+            uint32_t subX = freqRound(x);
+            uint32_t subY = 0;
+            SkPackedGlyphID correctID(3, subX, subY);
+            SkASSERT(packedID == correctID);
+            REPORTER_ASSERT(reporter, packedID == correctID);
+        }
+    }
+
+    {
+        // No subpixel positioning.
+        auto roundingSpec = SkGlyphPositionRoundingSpec(false, kNone_SkAxisAlignment);
+        SkIPoint mask = roundingSpec.ignorePositionFieldMask;
+        for (int y = -testLimit; y < testLimit; y++) {
+            for (int x = -testLimit; x < testLimit; x++) {
+                float fx = x * step, fy = y * step;
+                SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
+                SkPackedGlyphID packedID{3, roundedPos, mask};
+                uint32_t subX = 0;
+                uint32_t subY = 0;
+                SkPackedGlyphID correctID(3, subX, subY);
+                REPORTER_ASSERT(reporter, packedID == correctID);
+            }
+        }
+    }
+
+    {
+        // Subpixel with no axis snapping.
+        auto roundingSpec = SkGlyphPositionRoundingSpec(true, kNone_SkAxisAlignment);
+        SkIPoint mask = roundingSpec.ignorePositionFieldMask;
+        for (int y = -testLimit; y < testLimit; y++) {
+            for (int x = -testLimit; x < testLimit; x++) {
+                float fx = x * step, fy = y * step;
+                SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
+                SkPackedGlyphID packedID{3, roundedPos, mask};
+                uint32_t subX = freqRound(x);
+                uint32_t subY = freqRound(y);
+                SkPackedGlyphID correctID(3, subX, subY);
+                REPORTER_ASSERT(reporter, packedID == correctID);
+            }
+        }
+    }
+
+    {
+        // Test dynamic range by transposing a large distance.
+        // Floating point numbers have 24 bits of precision. The largest distance is 24 - 2 (for
+        // sub-pixel) - 1 (for truncation to floor trick in the code). This leaves 21 bits. Large
+        // Distance is 2^21 - 2 (because the test is on the interval [-2, 2).
+        const uint32_t kLogLargeDistance = 24 - PG::kSubPixelPosLen - 1;
+        const int64_t kLargeDistance = (1ull << kLogLargeDistance) - 2;
+        auto roundingSpec = SkGlyphPositionRoundingSpec(true, kNone_SkAxisAlignment);
+        SkIPoint mask = roundingSpec.ignorePositionFieldMask;
+        for (int y = -32; y < 33; y++) {
+            for (int x = -32; x < 33; x++) {
+                float fx = x * step + kLargeDistance, fy = y * step + kLargeDistance;
+                SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
+                SkPackedGlyphID packedID{3, roundedPos, mask};
+                uint32_t subX = freqRound(x);
+                uint32_t subY = freqRound(y);
+                SkPackedGlyphID correctID(3, subX, subY);
+                REPORTER_ASSERT(reporter, packedID == correctID);
+            }
+        }
+    }
+}
 
 DEF_TEST(SkSourceGlyphBufferBasic, reporter) {
     SkSourceGlyphBuffer rejects;
@@ -20,9 +103,7 @@
     auto source = SkMakeZip(glyphIDs, positions);
 
     rejects.setSource(source);
-    for (auto t : SkMakeEnumerate(rejects.source())) {
-        size_t i; SkGlyphID glyphID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+    for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
         REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
     }
@@ -31,9 +112,7 @@
     rejects.reject(2, 100);
     rejects.flipRejectsToSource();
     REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 100);
-    for (auto t : SkMakeEnumerate(rejects.source())) {
-        size_t i; SkGlyphID glyphID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+    for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
         // This will index 1 and 2 from the original source.
         size_t j = i + 1;
         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
@@ -44,9 +123,7 @@
     rejects.reject(0, 10);
     rejects.flipRejectsToSource();
     REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 10);
-    for (auto t : SkMakeEnumerate(rejects.source())) {
-        size_t i; SkGlyphID glyphID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+    for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
         // This will index 1 from the original source.
         size_t j = i + 1;
         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
@@ -55,9 +132,7 @@
 
     // Start all over
     rejects.setSource(source);
-    for (auto t : SkMakeEnumerate(rejects.source())) {
-        size_t i; SkGlyphID glyphID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+    for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
         REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
     }
@@ -67,9 +142,7 @@
     rejects.reject(2, 100);
     rejects.flipRejectsToSource();
     REPORTER_ASSERT(reporter, rejects.rejectedMaxDimension() == 100);
-    for (auto t : SkMakeEnumerate(rejects.source())) {
-        size_t i; SkGlyphID glyphID; SkPoint pos;
-        std::forward_as_tuple(i, std::tie(glyphID, pos)) = t;
+    for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
         // This will index 1 and 2 from the original source.
         size_t j = i + 1;
         REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
@@ -88,10 +161,8 @@
         SkDrawableGlyphBuffer drawable;
         drawable.ensureSize(100);
         drawable.startSource(source, {100, 100});
-        for (auto t : SkMakeEnumerate(drawable.input())) {
-            size_t i; SkGlyphVariant packedID; SkPoint pos;
-            std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
-            REPORTER_ASSERT(reporter, packedID.packedID().value() == glyphIDs[i]);
+        for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
+            REPORTER_ASSERT(reporter, packedID.packedID().glyphID() == glyphIDs[i]);
             REPORTER_ASSERT(reporter, pos == positions[i] + SkPoint::Make(100, 100));
         }
     }
@@ -102,11 +173,10 @@
         SkMatrix matrix = SkMatrix::MakeScale(0.5);
         SkGlyphPositionRoundingSpec rounding{true, kX_SkAxisAlignment};
         drawable.startDevice(source, {100, 100}, matrix, rounding);
-        for (auto t : SkMakeEnumerate(drawable.input())) {
-            size_t i; SkGlyphVariant packedID; SkPoint pos;
-            std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
+        for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
             REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID());
-            REPORTER_ASSERT(reporter, pos.x() == positions[i].x() * 0.5 + 50 + 0.125);
+            REPORTER_ASSERT(reporter,
+                    pos.x() == positions[i].x() * 0.5 + 50 + SkPackedGlyphID::kSubpixelRound);
             REPORTER_ASSERT(reporter, pos.y() == positions[i].y() * 0.5 + 50 + 0.5);
         }
     }
@@ -115,14 +185,10 @@
         SkDrawableGlyphBuffer drawable;
         drawable.ensureSize(100);
         drawable.startSource(source, {100, 100});
-        for (auto t : SkMakeEnumerate(drawable.input())) {
-            size_t i; SkGlyphVariant packedID; SkPoint pos;
-            std::forward_as_tuple(i, std::tie(packedID, pos)) = t;
+        for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
             drawable.push_back(&glyphs[i], i);
         }
-        for (auto t : SkMakeEnumerate(drawable.drawable())) {
-            size_t i; SkGlyphVariant glyph; SkPoint pos;
-            std::forward_as_tuple(i, std::tie(glyph, pos)) = t;
+        for (auto [i, glyph, pos] : SkMakeEnumerate(drawable.drawable())) {
             REPORTER_ASSERT(reporter, glyph.glyph() == &glyphs[i]);
         }
     }
diff --git a/tests/SkParagraphTest.cpp b/tests/SkParagraphTest.cpp
index 3d3e300..20f5824 100644
--- a/tests/SkParagraphTest.cpp
+++ b/tests/SkParagraphTest.cpp
@@ -1,20 +1,30 @@
 // Copyright 2019 Google LLC.
-#include "src/utils/SkOSPath.h"
+#include "src/core/SkFontMgrPriv.h"
 #include <sstream>
 #include "modules/skparagraph/include/TypefaceFontProvider.h"
 #include "modules/skparagraph/src/ParagraphBuilderImpl.h"
 #include "modules/skparagraph/src/ParagraphImpl.h"
+#include "modules/skparagraph/utils/TestFontCollection.h"
 #include "src/core/SkOSFile.h"
+#include "src/utils/SkOSPath.h"
 #include "src/utils/SkShaperJSONWriter.h"
 #include "tests/CodecPriv.h"
 #include "tests/Test.h"
 #include "tools/Resources.h"
 #include "tools/ToolUtils.h"
 
+#include "third_party/icu/SkLoadICU.h"
+
 #define VeryLongCanvasWidth 1000000
 #define TestCanvasWidth 1000
 #define TestCanvasHeight 600
 
+#define DEF_TEST_DISABLED(name, reporter) \
+static void test_##name(skiatest::Reporter* reporter, const GrContextOptions&); \
+skiatest::TestRegistry name##TestRegistry(skiatest::Test(#name, false, test_##name)); \
+void test_##name(skiatest::Reporter* reporter, const GrContextOptions&) { /* SkDebugf("Disabled:"#name "\n"); */ } \
+void disabled_##name(skiatest::Reporter* reporter, const GrContextOptions&)
+
 using namespace skia::textlayout;
 namespace {
 
@@ -25,20 +35,24 @@
 SkScalar EPSILON5 = 0.20f;
 SkScalar EPSILON2 = 0.50f;
 
-SkScalar halfLetterDiff = 0.0f;
-
 bool equal(const char* base, TextRange a, const char* b) {
     return std::strncmp(b, base + a.start, a.width()) == 0;
 }
-class TestFontCollection : public FontCollection {
+
+class ResourceFontCollection : public FontCollection {
 public:
-    TestFontCollection()
+    ResourceFontCollection(bool testOnly = false)
             : fFontsFound(false)
             , fResolvedFonts(0)
             , fResourceDir(GetResourcePath("fonts").c_str())
             , fFontProvider(sk_make_sp<TypefaceFontProvider>()) {
         std::vector<SkString> fonts;
         SkOSFile::Iter iter(fResourceDir.c_str());
+
+        if (!SkLoadICU()) {
+            SkDebugf("ICU not loaded, skipping all the tests\n");
+            return;
+        }
         SkString path;
         while (iter.next(&path)) {
             if (path.endsWith("Roboto-Italic.ttf")) {
@@ -48,6 +62,7 @@
         }
 
         if (!fFontsFound) {
+            // SkDebugf("Fonts not found, skipping all the tests\n");
             return;
         }
         // Only register fonts if we have to
@@ -57,13 +72,15 @@
             fFontProvider->registerTypeface(SkTypeface::MakeFromFile(file_path.c_str()));
         }
 
-        this->setAssetFontManager(std::move(fFontProvider));
+        if (testOnly) {
+            this->setTestFontManager(std::move(fFontProvider));
+        } else {
+            this->setAssetFontManager(std::move(fFontProvider));
+        }
         this->disableFontFallback();
-
-        if (!fFontsFound) SkDebugf("Fonts not found, skipping all the tests\n");
     }
 
-    ~TestFontCollection() = default;
+    ~ResourceFontCollection() = default;
 
     size_t resolvedFonts() const { return fResolvedFonts; }
 
@@ -143,7 +160,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_SimpleParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     const char* text = "Hello World Text Dialog";
     const size_t len = strlen(text);
@@ -161,6 +178,7 @@
 
     auto paragraph = builder.Build();
     paragraph->layout(TestCanvasWidth);
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
 
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -181,7 +199,7 @@
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -258,30 +276,30 @@
 
     REPORTER_ASSERT(reporter, boxes.size() == 7);
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 50, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 90.921f + 50 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 90.921f + 50, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 100, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.left(), 231.343f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.left(), 231.343f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.top(), 50, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.right(), 231.343f + 50 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.right(), 231.343f + 50, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[3].rect.bottom(), 100, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.left(), 281.343f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.left(), 281.343f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.right(), 281.343f + 5 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.right(), 281.343f + 5, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[4].rect.bottom(), 50, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.left(), 336.343f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.left(), 336.343f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.right(), 336.343f + 5 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.right(), 336.343f + 5, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[6].rect.bottom(), 50, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBaselineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBaselineParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -318,9 +336,9 @@
     canvas.drawRects(SK_ColorRED, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
@@ -330,15 +348,15 @@
     canvas.drawRects(SK_ColorBLUE, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 14.226f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 44.694f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderAboveBaselineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderAboveBaselineParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -375,9 +393,9 @@
     canvas.drawRects(SK_ColorRED, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.347f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 49.652f, EPSILON100));
 
     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
@@ -387,15 +405,15 @@
     canvas.drawRects(SK_ColorBLUE, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 25.531f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBelowBaselineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBelowBaselineParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -432,9 +450,9 @@
     canvas.drawRects(SK_ColorRED, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 24, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 74, EPSILON100));
 
     RectHeightStyle rect_height_style = RectHeightStyle::kTight;
@@ -444,15 +462,15 @@
     canvas.drawRects(SK_ColorBLUE, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), -0.121f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f - halfLetterDiff, EPSILON2));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.347f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBottomParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBottomParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -491,23 +509,23 @@
     auto boxes = paragraph->getRectsForPlaceholders();
     canvas.drawRects(SK_ColorRED, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
     boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 19.531f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderTopParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderTopParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -546,23 +564,23 @@
     auto boxes = paragraph->getRectsForPlaceholders();
     canvas.drawRects(SK_ColorRED, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
     boxes = paragraph->getRectsForRange(0, 1, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 0.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 16.097f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 30.468f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderMiddleParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderMiddleParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -601,23 +619,23 @@
     auto boxes = paragraph->getRectsForPlaceholders();
     canvas.drawRects(SK_ColorRED, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55 - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f + 55, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 75.324f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 9.765f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 40.234f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderIdeographicBaselineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderIdeographicBaselineParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -655,23 +673,23 @@
     auto boxes = paragraph->getRectsForPlaceholders();
     canvas.drawRects(SK_ColorRED, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 162.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 162.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f + 55 - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f + 55, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
     boxes = paragraph->getRectsForRange(5, 6, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 135.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 135.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 4.703f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 162.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 42.065f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderBreakParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderBreakParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -778,9 +796,9 @@
     boxes = paragraph->getRectsForRange(175, 176, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorGREEN, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 1);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 31.695f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 31.695f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 218.531f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 47.292f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 47.292f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 249, EPSILON100));
 
     boxes = paragraph->getRectsForPlaceholders();
@@ -789,25 +807,25 @@
     boxes = paragraph->getRectsForRange(4, 45, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
     REPORTER_ASSERT(reporter, boxes.size() == 30);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 59.726f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 59.726f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 26.378f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 56.847f, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.left(), 606.343f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.left(), 606.343f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.top(), 38, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.right(), 631.343f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.right(), 631.343f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[11].rect.bottom(), 63, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.left(), 0.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.left(), 0.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.top(), 63.5f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.right(), 50.5f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.right(), 50.5f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[17].rect.bottom(), 113.5f, EPSILON100));
 }
 
 // Checked: DIFF+ (half of the letter spacing before the text???)
 DEF_TEST(SkParagraph_InlinePlaceholderGetRectsParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_InlinePlaceholderGetRectsParagraph.png");
     if (!fontCollection->fontsFound()) return;
 
@@ -899,44 +917,44 @@
     canvas.drawRects(SK_ColorRED, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 34);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 90.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 140.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 140.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 50, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.left(), 800.921f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.left(), 800.921f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.top(), 0, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.right(), 850.921f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.right(), 850.921f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[16].rect.bottom(), 50, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.left(), 503.382f - halfLetterDiff, EPSILON10));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.left(), 503.382f, EPSILON10));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.top(), 160, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.right(), 508.382f - halfLetterDiff, EPSILON10));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.right(), 508.382f, EPSILON10));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[33].rect.bottom(), 180, EPSILON100));
 
     boxes = paragraph->getRectsForRange(30, 50, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
 
     REPORTER_ASSERT(reporter, boxes.size() == 8);
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 216.097f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.left(), 216.097f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.top(), 60, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 290.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.right(), 290.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[0].rect.bottom(), 120, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 290.921f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.left(), 290.921f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.top(), 60, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 340.921f - halfLetterDiff, EPSILON20));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.right(), 340.921f, EPSILON20));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[1].rect.bottom(), 120, EPSILON100));
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.left(), 340.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.left(), 340.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.top(), 60, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.right(), 345.921f - halfLetterDiff, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.right(), 345.921f, EPSILON50));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(boxes[2].rect.bottom(), 120, EPSILON100));
 }
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_SimpleRedParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     const char* text = "I am RED";
     const size_t len = strlen(text);
@@ -954,6 +972,7 @@
 
     auto paragraph = builder.Build();
     paragraph->layout(TestCanvasWidth);
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
 
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -974,7 +993,7 @@
 
 // Checked: DIFF+ (Space between 1 & 2 style blocks)
 DEF_TEST(SkParagraph_RainbowParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     TestCanvas canvas("SkParagraph_RainbowParagraph.png");
     if (!fontCollection->fontsFound()) return;
     const char* text1 = "Red Roboto"; // [0:10)
@@ -1040,6 +1059,8 @@
     paragraph->layout(1000);
     paragraph->paint(canvas.get(), 0, 0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
     REPORTER_ASSERT(reporter, impl->runs().size() == 4);
     REPORTER_ASSERT(reporter, impl->styles().size() == 4);
@@ -1096,7 +1117,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_DefaultStyleParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_DefaultStyleParagraph.png");
     const char* text = "No TextStyle! Uh Oh!";
@@ -1114,6 +1135,8 @@
     paragraph->layout(TestCanvasWidth);
     paragraph->paint(canvas.get(), 10.0, 15.0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -1134,7 +1157,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_BoldParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_BoldParagraph.png");
     const char* text = "This is Red max bold text!";
@@ -1159,6 +1182,8 @@
     paragraph->layout(VeryLongCanvasWidth);
     paragraph->paint(canvas.get(), 10.0, 60.0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -1179,7 +1204,7 @@
 
 // Checked: NO DIFF (line height rounding error)
 DEF_TEST(SkParagraph_HeightOverrideParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_HeightOverrideParagraph.png");
     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
@@ -1204,7 +1229,7 @@
     paragraph->layout(550);
 
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
-    REPORTER_ASSERT(reporter, impl->runs().size() == 3);
+    REPORTER_ASSERT(reporter, impl->runs().size() == 5);
     REPORTER_ASSERT(reporter, impl->styles().size() == 1);  // paragraph style does not count
     REPORTER_ASSERT(reporter, impl->styles()[0].fStyle.equals(text_style));
 
@@ -1235,7 +1260,7 @@
 
 // Checked: DIFF+
 DEF_TEST(SkParagraph_LeftAlignParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_LeftAlignParagraph.png");
     const char* text =
@@ -1320,7 +1345,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_RightAlignParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_RightAlignParagraph.png");
     const char* text =
@@ -1408,7 +1433,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_CenterAlignParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_CenterAlignParagraph.png");
     const char* text =
@@ -1496,7 +1521,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_JustifyAlignParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_JustifyAlignParagraph.png");
     const char* text =
@@ -1584,7 +1609,7 @@
 
 // Checked: DIFF (ghost spaces as a separate box in TxtLib)
 DEF_TEST(SkParagraph_JustifyRTL, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>(true);
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_JustifyRTL.png");
     const char* text =
@@ -1634,7 +1659,7 @@
     RectWidthStyle rect_width_style = RectWidthStyle::kTight;
     auto boxes = paragraph->getRectsForRange(0, 100, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorRED, boxes);
-    REPORTER_ASSERT(reporter, boxes.size() == 3); // DIFF
+    REPORTER_ASSERT(reporter, boxes.size() == 5);
 
     boxes = paragraph->getRectsForRange(240, 250, rect_height_style, rect_width_style);
     canvas.drawRects(SK_ColorBLUE, boxes);
@@ -1648,7 +1673,7 @@
 
 // Checked: NO DIFF (some minor decoration differences, probably)
 DEF_TEST(SkParagraph_DecorationsParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_DecorationsParagraph.png");
     const char* text1 = "This text should be";
@@ -1767,12 +1792,12 @@
 }
 
 DEF_TEST(SkParagraph_WavyDecorationParagraph, reporter) {
-    SkDebugf("TODO: Fix decorations\n");
+    // SkDebugf("TODO: Fix decorations\n");
 }
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_ItalicsParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ItalicsParagraph.png");
     const char* text1 = "No italic ";
@@ -1837,7 +1862,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_ChineseParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ChineseParagraph.png");
     const char* text =
@@ -1874,6 +1899,8 @@
     paragraph->layout(TestCanvasWidth - 100);
     paragraph->paint(canvas.get(), 0, 0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -1884,7 +1911,7 @@
 
 // Checked: NO DIFF (disabled)
 DEF_TEST(SkParagraph_ArabicParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ArabicParagraph.png");
     const char* text =
@@ -1917,6 +1944,8 @@
     paragraph->layout(TestCanvasWidth - 100);
     paragraph->paint(canvas.get(), 0, 0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
@@ -1928,7 +1957,7 @@
 // Checked: DIFF (2 boxes and each space is a word)
 DEF_TEST(SkParagraph_ArabicRectsParagraph, reporter) {
 
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ArabicRectsParagraph.png");
     const char* text = "بمباركة التقليدية قام عن. تصفح يد    ";
@@ -1976,7 +2005,7 @@
 // Checked DIFF+
 DEF_TEST(SkParagraph_ArabicRectsLTRLeftAlignParagraph, reporter) {
 
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ArabicRectsLTRLeftAlignParagraph.png");
     const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
@@ -2023,7 +2052,7 @@
 // Checked DIFF+
 DEF_TEST(SkParagraph_ArabicRectsLTRRightAlignParagraph, reporter) {
 
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ArabicRectsLTRRightAlignParagraph.png");
     const char* text = "Helloبمباركة التقليدية قام عن. تصفح يد ";
@@ -2070,7 +2099,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetGlyphPositionAtCoordinateParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetGlyphPositionAtCoordinateParagraph.png");
     const char* text =
@@ -2135,7 +2164,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeParagraph.png");
     const char* text =
@@ -2232,7 +2261,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeTight, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeTight.png");
     const char* text =
@@ -2298,7 +2327,7 @@
 
 // Checked: DIFF+
 DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingMiddle.png");
     const char* text =
@@ -2420,7 +2449,7 @@
 
 // Checked: NO DIFF+
 DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingTop, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingTop.png");
     const char* text =
@@ -2542,7 +2571,7 @@
 
 // Checked: NO DIFF+
 DEF_TEST(SkParagraph_GetRectsForRangeIncludeLineSpacingBottom, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeLineSpacingBottom.png");
     const char* text =
@@ -2664,7 +2693,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeIncludeCombiningCharacter, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeIncludeCombiningCharacter.png");
     const char* text = "ดีสวัสดีชาวโลกที่น่ารัก";
@@ -2727,7 +2756,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeCenterParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraph.png");
     // Minikin uses a hard coded list of unicode characters that he treats as invisible - as spaces.
@@ -2825,7 +2854,7 @@
 
 // Checked DIFF+
 DEF_TEST(SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterParagraphNewlineCentered.png");
     const char* text = "01234\n";
@@ -2887,7 +2916,7 @@
 
 // Checked NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeCenterMultiLineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeCenterMultiLineParagraph.png");
     const char* text = "01234    \n0123         "; // includes ideographic space and english space.
@@ -2989,7 +3018,7 @@
 
 // Checked: DIFF (line height rounding error)
 DEF_TEST(SkParagraph_GetRectsForRangeStrut, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeStrut.png");
     const char* text = "Chinese 字典";
@@ -3036,7 +3065,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_GetRectsForRangeStrutFallback, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetRectsForRangeStrutFallback.png");
     const char* text = "Chinese 字典";
@@ -3076,7 +3105,7 @@
 
 // Checked: DIFF (small in numbers)
 DEF_TEST(SkParagraph_GetWordBoundaryParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_GetWordBoundaryParagraph.png");
     const char* text = "12345  67890 12345 67890 12345 67890 12345 "
@@ -3152,7 +3181,7 @@
 
 // Checked: DIFF (unclear)
 DEF_TEST(SkParagraph_SpacingParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_SpacingParagraph.png");
     ParagraphStyle paragraph_style;
@@ -3235,7 +3264,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_LongWordParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_LongWordParagraph.png");
     const char* text =
@@ -3278,7 +3307,7 @@
 
 // Checked: DIFF?
 DEF_TEST(SkParagraph_KernScaleParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_KernScaleParagraph.png");
 
@@ -3324,7 +3353,7 @@
 
 // Checked: DIFF+
 DEF_TEST(SkParagraph_NewlineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_NewlineParagraph.png");
     const char* text =
@@ -3365,7 +3394,7 @@
 
 // TODO: Fix underline
 DEF_TEST(SkParagraph_EmojiParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_EmojiParagraph.png");
   const char* text =
@@ -3392,6 +3421,8 @@
     paragraph->layout(TestCanvasWidth);
     paragraph->paint(canvas.get(), 0, 0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     REPORTER_ASSERT(reporter, impl->lines().size() == 8);
@@ -3407,7 +3438,7 @@
 
 // Checked: DIFF+
 DEF_TEST(SkParagraph_EmojiMultiLineRectsParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_EmojiMultiLineRectsParagraph.png");
   const char* text =
@@ -3471,7 +3502,7 @@
 
 // Checked: DIFF (line breaking)
 DEF_TEST(SkParagraph_RepeatLayoutParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_RepeatLayoutParagraph.png");
     const char* text =
@@ -3511,7 +3542,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_Ellipsize, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_Ellipsize.png");
     const char* text =
@@ -3549,7 +3580,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_UnderlineShiftParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_UnderlineShiftParagraph.png");
     const char* text1 = "fluttser ";
@@ -3621,7 +3652,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_SimpleShadow, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_SimpleShadow.png");
     const char* text = "Hello World Text Dialog";
@@ -3659,7 +3690,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_ComplexShadow, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_ComplexShadow.png");
     const char* text = "Text Chunk ";
@@ -3729,7 +3760,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_BaselineParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_BaselineParagraph.png");
     const char* text =
@@ -3774,9 +3805,9 @@
                     SkScalarNearlyEqual(paragraph->getAlphabeticBaseline(), 63.305f, EPSILON100));
 }
 
-// Checked: NO DIFF
+// Checked: NO DIFF (number of runs only)
 DEF_TEST(SkParagraph_FontFallbackParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_FontFallbackParagraph.png");
 
@@ -3833,35 +3864,36 @@
     builder.pop();
 
     auto paragraph = builder.Build();
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == -1); // Not shaped yet
     paragraph->layout(TestCanvasWidth);
     paragraph->paint(canvas.get(), 10.0, 15.0);
 
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 2); // From the text1
+
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
     // Font resolution in Skia produces 6 runs because 2 parts of "Roboto 字典 " have different
     // script (Minikin merges the first 2 into one because of unresolved) [Apple + Unresolved ]
     // [Apple + Noto] [Apple + Han]
-    REPORTER_ASSERT(reporter, impl->runs().size() == 6);
+    REPORTER_ASSERT(reporter, impl->runs().size() == 7);
 
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[0].advance().fX, 48.330f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[1].advance().fX, 15.879f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[2].advance().fX, 139.125f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[3].advance().fX, 27.999f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[4].advance().fX, 62.248f, EPSILON100));
-    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[5].advance().fX, 27.999f, EPSILON100));
+    auto robotoAdvance = impl->runs()[0].advance().fX +
+                         impl->runs()[1].advance().fX +
+                         impl->runs()[2].advance().fX;
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(robotoAdvance, 64.199f, EPSILON50));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[3].advance().fX, 139.125f, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[4].advance().fX, 27.999f, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[5].advance().fX, 62.248f, EPSILON100));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(impl->runs()[6].advance().fX, 27.999f, EPSILON100));
 
     // When a different font is resolved, then the metrics are different.
-    REPORTER_ASSERT(reporter, impl->runs()[1].correctAscent() != impl->runs()[3].correctAscent());
-    REPORTER_ASSERT(reporter, impl->runs()[1].correctDescent() != impl->runs()[3].correctDescent());
-    REPORTER_ASSERT(reporter, impl->runs()[3].correctAscent() != impl->runs()[5].correctAscent());
-    REPORTER_ASSERT(reporter, impl->runs()[3].correctDescent() != impl->runs()[5].correctDescent());
-    REPORTER_ASSERT(reporter, impl->runs()[1].correctAscent() != impl->runs()[5].correctAscent());
-    REPORTER_ASSERT(reporter, impl->runs()[1].correctDescent() != impl->runs()[5].correctDescent());
+    REPORTER_ASSERT(reporter, impl->runs()[4].correctAscent() != impl->runs()[6].correctAscent());
+    REPORTER_ASSERT(reporter, impl->runs()[4].correctDescent() != impl->runs()[6].correctDescent());
 }
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_StrutParagraph1, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_StrutParagraph1.png");
     // The chinese extra height should be absorbed by the strut.
@@ -3966,7 +3998,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_StrutParagraph2, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_StrutParagraph2.png");
     // The chinese extra height should be absorbed by the strut.
@@ -4073,7 +4105,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_StrutParagraph3, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_StrutParagraph3.png");
 
@@ -4181,7 +4213,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_StrutForceParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_StrutForceParagraph.png");
     const char* text = "01234満毎冠行来昼本可\nabcd\n満毎冠行来昼本可";
@@ -4280,7 +4312,7 @@
 
 // Checked: NO DIFF
 DEF_TEST(SkParagraph_StrutDefaultParagraph, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_StrutDefaultParagraph.png");
 
@@ -4344,12 +4376,12 @@
 
 // TODO: Implement font features
 DEF_TEST(SkParagraph_FontFeaturesParagraph, reporter) {
-    SkDebugf("TODO: Font features\n");
+    // SkDebugf("TODO: Font features\n");
 }
 
 // Not in Minikin
 DEF_TEST(SkParagraph_WhitespacesInMultipleFonts, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     const char* text = "English English 字典 字典 😀😃😄 😀😃😄";
     const size_t len = strlen(text);
@@ -4369,14 +4401,18 @@
     auto paragraph = builder.Build();
     paragraph->layout(TestCanvasWidth);
 
-    SkDEBUGCODE(auto impl = static_cast<ParagraphImpl*>(paragraph.get());)
-            SkASSERT(impl->runs().size() == 3);
-    SkASSERT(impl->runs()[0].textRange().end == impl->runs()[1].textRange().start);
-    SkASSERT(impl->runs()[1].textRange().end == impl->runs()[2].textRange().start);
+    REPORTER_ASSERT(reporter, paragraph->unresolvedGlyphs() == 0);
+
+    auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+    for (size_t i = 0; i < impl->runs().size() - 1; ++i) {
+        auto first = impl->runs()[i].textRange();
+        auto next  = impl->runs()[i + 1].textRange();
+        REPORTER_ASSERT(reporter, first.end == next.start);
+    }
 }
 
 DEF_TEST(SkParagraph_JSON1, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     const char* text = "👨‍👩‍👧‍👦";
     const size_t len = strlen(text);
@@ -4396,7 +4432,7 @@
 
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
-    auto run = impl->runs().front();
+    auto& run = impl->runs().front();
 
     auto cluster = 0;
     SkShaperJSONWriter::VisualizeClusters(
@@ -4410,11 +4446,11 @@
                 }
                 ++cluster;
             });
-    SkASSERT(cluster <= 2);
+    REPORTER_ASSERT(reporter, cluster <= 2);
 }
 
 DEF_TEST(SkParagraph_JSON2, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     const char* text = "p〠q";
     const size_t len = strlen(text);
@@ -4436,7 +4472,6 @@
 
     auto impl = static_cast<ParagraphImpl*>(paragraph.get());
     REPORTER_ASSERT(reporter, impl->runs().size() == 1);
-    auto run = impl->runs().front();
 
     auto cluster = 0;
     for (auto& run : impl->runs()) {
@@ -4454,12 +4489,13 @@
                 });
     }
 
-    SkASSERT(cluster <= 2);
+    REPORTER_ASSERT(reporter, cluster <= 2);
 }
 
 DEF_TEST(SkParagraph_CacheText, reporter) {
     ParagraphCache cache;
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    cache.turnOn(true);
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
 
     ParagraphStyle paragraph_style;
@@ -4493,7 +4529,8 @@
 
 DEF_TEST(SkParagraph_CacheFonts, reporter) {
     ParagraphCache cache;
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    cache.turnOn(true);
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
 
     ParagraphStyle paragraph_style;
@@ -4513,8 +4550,6 @@
         auto paragraph = builder.Build();
         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
-        impl->getResolver().findAllFontsForAllStyledBlocks(impl);
-
         REPORTER_ASSERT(reporter, count == cache.count());
         auto found = cache.findParagraph(impl);
         REPORTER_ASSERT(reporter, found == expectedToBeFound);
@@ -4534,7 +4569,8 @@
 
 DEF_TEST(SkParagraph_CacheFontRanges, reporter) {
     ParagraphCache cache;
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    cache.turnOn(true);
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
 
     ParagraphStyle paragraph_style;
@@ -4562,8 +4598,6 @@
         auto paragraph = builder.Build();
         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
-        impl->getResolver().findAllFontsForAllStyledBlocks(impl);
-
         REPORTER_ASSERT(reporter, count == cache.count());
         auto found = cache.findParagraph(impl);
         REPORTER_ASSERT(reporter, found == expectedToBeFound);
@@ -4580,7 +4614,8 @@
 
 DEF_TEST(SkParagraph_CacheStyles, reporter) {
     ParagraphCache cache;
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    cache.turnOn(true);
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
 
     ParagraphStyle paragraph_style;
@@ -4601,8 +4636,6 @@
         auto paragraph = builder.Build();
         auto impl = static_cast<ParagraphImpl*>(paragraph.get());
 
-        impl->getResolver().findAllFontsForAllStyledBlocks(impl);
-
         REPORTER_ASSERT(reporter, count == cache.count());
         auto found = cache.findParagraph(impl);
         REPORTER_ASSERT(reporter, found == expectedToBeFound);
@@ -4620,7 +4653,7 @@
 }
 
 DEF_TEST(SkParagraph_EmptyParagraphWithLineBreak, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
     TestCanvas canvas("SkParagraph_EmptyParagraphWithLineBreak.png");
@@ -4639,7 +4672,7 @@
 }
 
 DEF_TEST(SkParagraph_NullInMiddleOfText, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
     TestCanvas canvas("SkParagraph_NullInMiddleOfText.png");
@@ -4659,7 +4692,7 @@
 }
 
 DEF_TEST(SkParagraph_PlaceholderOnly, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     TestCanvas canvas("SkParagraph_PlaceholderOnly.png");
 
@@ -4676,7 +4709,7 @@
 }
 
 DEF_TEST(SkParagraph_Fallbacks, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault(), "Arial");
     TestCanvas canvas("SkParagraph_Fallbacks.png");
@@ -4719,9 +4752,10 @@
 }
 
 DEF_TEST(SkParagraph_Bidi1, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+    fontCollection->enableFontFallback();
     TestCanvas canvas("SkParagraph_Bidi1.png");
 
     std::u16string abc = u"\u202Dabc";
@@ -4770,9 +4804,10 @@
 }
 
 DEF_TEST(SkParagraph_Bidi2, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+    fontCollection->enableFontFallback();
     TestCanvas canvas("SkParagraph_Bidi2.png");
 
     std::u16string abcD = u"\u202Dabc\u202ED";
@@ -4813,7 +4848,7 @@
 }
 
 DEF_TEST(SkParagraph_NewlineOnly, reporter) {
-    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>();
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
     if (!fontCollection->fontsFound()) return;
     fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
     TestCanvas canvas("SkParagraph_Newline.png");
@@ -4832,3 +4867,187 @@
     paragraph->layout(1000);
     REPORTER_ASSERT(reporter, paragraph->getHeight() == 28);
 }
+
+DEF_TEST_DISABLED(SkParagraph_FontResolutions, reporter) {
+    TestCanvas canvas("SkParagraph_FontResolutions.png");
+
+    sk_sp<TestFontCollection> fontCollection =
+            sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false);
+    if (!fontCollection->fontsFound()) return;
+
+    if (!fontCollection->addFontFromFile("abc/abc.ttf", "abc")) {
+        return;
+    }
+    if (!fontCollection->addFontFromFile("abc/abc+grave.ttf", "abc+grave")) {
+        return;
+    }
+    if (!fontCollection->addFontFromFile("abc/abc_agrave.ttf", "abc_agrave")) {
+        return;
+    }
+
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("abc")});
+    text_style.setFontSize(50);
+
+    ParagraphStyle paragraph_style;
+    ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+
+    text_style.setFontFamilies({SkString("abc"), SkString("abc+grave")});
+    text_style.setColor(SK_ColorBLUE);
+    builder.pushStyle(text_style);
+    builder.addText(u"a\u0300");
+    text_style.setColor(SK_ColorMAGENTA);
+    builder.pushStyle(text_style);
+    builder.addText(u"à");
+
+    text_style.setFontFamilies({SkString("abc"), SkString("abc_agrave")});
+
+    text_style.setColor(SK_ColorRED);
+    builder.pushStyle(text_style);
+    builder.addText(u"a\u0300");
+    text_style.setColor(SK_ColorGREEN);
+    builder.pushStyle(text_style);
+    builder.addText(u"à");
+
+    auto paragraph = builder.Build();
+    paragraph->layout(TestCanvasWidth);
+
+    auto impl = static_cast<ParagraphImpl*>(paragraph.get());
+    REPORTER_ASSERT(reporter, impl->runs().size() == 2);
+
+    REPORTER_ASSERT(reporter, impl->runs().front().size() == 4);
+    REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[0] == impl->runs().front().glyphs()[2]);
+    REPORTER_ASSERT(reporter, impl->runs().front().glyphs()[1] == impl->runs().front().glyphs()[3]);
+
+    REPORTER_ASSERT(reporter, impl->runs().back().size() == 2);
+    REPORTER_ASSERT(reporter, impl->runs().back().glyphs()[0] == impl->runs().back().glyphs()[1]);
+
+    paragraph->paint(canvas.get(), 100, 100);
+}
+
+DEF_TEST_DISABLED(SkParagraph_FontStyle, reporter) {
+    TestCanvas canvas("SkParagraph_FontStyle.png");
+
+    sk_sp<TestFontCollection> fontCollection = sk_make_sp<TestFontCollection>(GetResourcePath("fonts").c_str(), false, true);
+    if (!fontCollection->fontsFound()) return;
+
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("Roboto")});
+    text_style.setColor(SK_ColorBLACK);
+    text_style.setFontSize(20);
+    SkFontStyle fs = SkFontStyle(
+        SkFontStyle::Weight::kLight_Weight,
+        SkFontStyle::Width::kNormal_Width,
+        SkFontStyle::Slant::kUpright_Slant
+    );
+    text_style.setFontStyle(fs);
+    ParagraphStyle paragraph_style;
+    paragraph_style.setTextStyle(text_style);
+    TextStyle boldItalic;
+    boldItalic.setFontFamilies({SkString("Roboto")});
+    boldItalic.setColor(SK_ColorRED);
+    SkFontStyle bi = SkFontStyle(
+        SkFontStyle::Weight::kBold_Weight,
+        SkFontStyle::Width::kNormal_Width,
+        SkFontStyle::Slant::kItalic_Slant
+    );
+    boldItalic.setFontStyle(bi);
+    ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+    builder.addText("Default text\n");
+    builder.pushStyle(boldItalic);
+    builder.addText("Bold and Italic\n");
+    builder.pop();
+    builder.addText("back to normal");
+    auto paragraph = builder.Build();
+    paragraph->layout(250);
+    paragraph->paint(canvas.get(), 0, 0);
+}
+
+DEF_TEST_DISABLED(SkParagraph_Shaping, reporter) {
+    TestCanvas canvas("SkParagraph_Shaping.png");
+
+    auto dir = "/usr/local/google/home/jlavrova/Sources/flutter/engine/src/out/host_debug_unopt_x86/gen/flutter/third_party/txt/assets";
+    sk_sp<TestFontCollection> fontCollection =
+            sk_make_sp<TestFontCollection>(dir, /*GetResourcePath("fonts").c_str(), */ false);
+    if (!fontCollection->fontsFound()) return;
+
+
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("Roboto")});
+    text_style.setColor(SK_ColorGRAY);
+    text_style.setFontSize(14);
+    SkFontStyle b = SkFontStyle(
+        SkFontStyle::Weight::kNormal_Weight,
+        SkFontStyle::Width::kNormal_Width,
+        SkFontStyle::Slant::kUpright_Slant
+    );
+    text_style.setFontStyle(b);
+    ParagraphStyle paragraph_style;
+    ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+    builder.pushStyle(text_style);
+    builder.addText("Eat0 apple0 pies0 | Eat1 apple1 pies1 | Eat2 apple2 pies2");
+    auto paragraph = builder.Build();
+    paragraph->layout(380);
+    paragraph->paint(canvas.get(), 0, 0);
+}
+
+DEF_TEST(SkParagraph_Ellipsis, reporter) {
+    sk_sp<ResourceFontCollection> fontCollection = sk_make_sp<ResourceFontCollection>();
+    if (!fontCollection->fontsFound()) return;
+    fontCollection->setDefaultFontManager(SkFontMgr::RefDefault());
+    TestCanvas canvas("SkParagraph_Ellipsis.png");
+
+    const char* text = "This\n"
+                       "is a wrapping test. It should wrap at manual newlines, and if softWrap is true, also at spaces.";
+    TextStyle text_style;
+    text_style.setFontFamilies({SkString("Ahem")});
+    text_style.setColor(SK_ColorBLACK);
+    text_style.setFontSize(10);
+
+    auto relayout = [&](size_t lines, bool ellipsis,
+            SkScalar width, SkScalar height, SkScalar minWidth, SkScalar maxWidth, SkColor bg) {
+        ParagraphStyle paragraph_style;
+        SkPaint paint;
+        paint.setColor(bg);
+        text_style.setForegroundColor(paint);
+        paragraph_style.setTextStyle(text_style);
+        paragraph_style.setMaxLines(lines);
+        if (ellipsis) {
+            paragraph_style.setEllipsis(u"\u2026");
+        }
+        ParagraphBuilderImpl builder(paragraph_style, fontCollection);
+        builder.addText(text);
+        auto paragraph = builder.Build();
+        paragraph->layout(50);
+        paragraph->paint(canvas.get(), 0, 0);
+        canvas.get()->translate(50, paragraph->getHeight() + 10);
+        auto result = paragraph->getRectsForRange(0, strlen(text), RectHeightStyle::kTight, RectWidthStyle::kTight);
+        SkPaint background;
+        background.setColor(SK_ColorRED);
+        background.setStyle(SkPaint::kStroke_Style);
+        background.setAntiAlias(true);
+        background.setStrokeWidth(1);
+        canvas.get()->drawRect(result.front().rect, background);
+
+        SkASSERT(width == paragraph->getMaxWidth());
+        SkASSERT(height == paragraph->getHeight());
+        SkASSERT(minWidth == paragraph->getMinIntrinsicWidth());
+        SkASSERT(maxWidth == paragraph->getMaxIntrinsicWidth());
+    };
+
+    SkPaint paint;
+    paint.setColor(SK_ColorLTGRAY);
+    canvas.get()->drawRect(SkRect::MakeXYWH(0, 0, 50, 500), paint);
+
+    relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
+    relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
+    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
+
+    relayout(1, true, 50, 10, 950, 950, SK_ColorYELLOW);
+    relayout(3, true, 50, 30,  50, 950, SK_ColorMAGENTA);
+    relayout(std::numeric_limits<size_t>::max(), true, 50, 20,  950, 950, SK_ColorCYAN);
+
+    relayout(1, false, 50, 10, 950, 950, SK_ColorRED);
+    relayout(3, false, 50, 30,  50, 950, SK_ColorBLUE);
+    relayout(std::numeric_limits<size_t>::max(), false, 50, 200,  50, 950, SK_ColorGREEN);
+}
diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp
index f56a4a3..fd327e8 100644
--- a/tests/SkRemoteGlyphCacheTest.cpp
+++ b/tests/SkRemoteGlyphCacheTest.cpp
@@ -803,9 +803,11 @@
 
         std::vector<uint8_t> serverStrikeData;
         server.writeStrikeData(&serverStrikeData);
-
-        REPORTER_ASSERT(reporter,
-                        client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
+        if (!serverStrikeData.empty()) {
+            REPORTER_ASSERT(reporter,
+                            client.readStrikeData(serverStrikeData.data(),
+                                                  serverStrikeData.size()));
+        }
         auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
         REPORTER_ASSERT(reporter, clientBlob);
 
@@ -889,12 +891,17 @@
     auto scalerProxy = static_cast<SkScalerContextProxy*>(testCache->getScalerContext());
     scalerProxy->initCache(testCache.get(), &strikeCache);
 
+    auto rounding = testCache->roundingSpec();
+    SkBulkGlyphMetricsAndImages metricsAndImages{std::move(testCache)};
+
     // Look for the lost glyph.
     {
         SkPoint pt{SkFixedToScalar(lostGlyphID.getSubXFixed()),
                    SkFixedToScalar(lostGlyphID.getSubYFixed())};
-        SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.glyphID(), pt);
-        testCache->prepareImage(lostGlyph);
+        SkPackedGlyphID packedID{
+            lostGlyphID.glyphID(), pt, rounding.ignorePositionFieldMask};
+
+        const SkGlyph* lostGlyph = metricsAndImages.glyph(packedID);
 
         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
@@ -906,8 +913,9 @@
     {
         SkPoint pt{SkFixedToScalar(SK_FixedQuarter),
                    SkFixedToScalar(SK_FixedQuarter)};
-        SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.glyphID(), pt);
-        testCache->prepareImage(lostGlyph);
+        SkPackedGlyphID packedID{
+                lostGlyphID.glyphID(), pt, rounding.ignorePositionFieldMask};
+        const SkGlyph* lostGlyph = metricsAndImages.glyph(packedID);
 
         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
@@ -945,7 +953,9 @@
     SkPaint paint;
     paint.setColor(SK_ColorRED);
 
-    auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
+    SkPoint glyphPos = {0.5f, 0.5f};
+    SkIPoint glyphIPos = {SkScalarToFixed(glyphPos.x()), SkScalarToFixed(glyphPos.y())};
+    auto lostGlyphID = SkPackedGlyphID(1, glyphIPos.x(), glyphIPos.y());
     const uint8_t glyphImage[] = {0xFF, 0xFF};
     SkMask::Format realMask;
     SkMask::Format fakeMask;
@@ -996,10 +1006,17 @@
         auto* cacheState = server.getOrCreateCache(
                 paint, font, SkSurfacePropsCopyOrDefault(nullptr),
                 SkMatrix::I(), flags, &effects);
-        SkStrikeServer::AddGlyphForTesting(cacheState, lostGlyphID, false);
-
+        SkGlyphID glyphID = lostGlyphID.glyphID();
+        SkZip<const SkGlyphID, const SkPoint> source{1, &glyphID, &glyphPos};
+        SkSourceGlyphBuffer rejects;
+        SkDrawableGlyphBuffer drawables;
+        drawables.ensureSize(source.size());
+        rejects.setSource(source);
+        drawables.startSource(rejects.source(), {0, 0});
+        SkStrikeServer::AddGlyphForTesting(cacheState, &drawables, &rejects);
         std::vector<uint8_t> serverStrikeData;
         server.writeStrikeData(&serverStrikeData);
+        REPORTER_ASSERT(reporter, !serverStrikeData.empty());
         REPORTER_ASSERT(reporter,
                         client.readStrikeData(
                                 serverStrikeData.data(),
diff --git a/tests/SkSLErrorTest.cpp b/tests/SkSLErrorTest.cpp
index 92151d8..1fbf29e 100644
--- a/tests/SkSLErrorTest.cpp
+++ b/tests/SkSLErrorTest.cpp
@@ -378,7 +378,7 @@
 DEF_TEST(SkSLNoReturn, r) {
     test_failure(r,
                  "int foo() { if (2 > 5) return 3; }",
-                 "error: 1: function can exit without returning a value\n1 error\n");
+                 "error: 1: function 'foo' can exit without returning a value\n1 error\n");
 }
 
 DEF_TEST(SkSLBreakOutsideLoop, r) {
diff --git a/tests/SkSLFPTest.cpp b/tests/SkSLFPTest.cpp
index ce6a971..7933668 100644
--- a/tests/SkSLFPTest.cpp
+++ b/tests/SkSLFPTest.cpp
@@ -454,9 +454,7 @@
             "SkString sk_TransformedCoords2D_0 = "
                            "fragBuilder->ensureCoords2D(args.fTransformedCoords[0].fVaryingPoint);",
             "fragBuilder->codeAppendf(\"%s = half4(%s, %s);\\n\", args.fOutputColor, "
-                    "_outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str() :"
-                    " \"_coords\", _outer.computeLocalCoordsInVertexShader() ? "
-                    "sk_TransformedCoords2D_0.c_str() : \"_coords\");"
+                    "sk_TransformedCoords2D_0.c_str(), sk_TransformedCoords2D_0.c_str());"
          });
 
 }
@@ -703,8 +701,7 @@
             "SkString sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D("
                                                      "args.fTransformedCoords[0].fVaryingPoint);\n",
             "SkString _coords110 = SkStringPrintf(\"%s / 2.0\", "
-                    "_outer.computeLocalCoordsInVertexShader() ? sk_TransformedCoords2D_0.c_str() :"
-                    " \"_coords\");\n",
+                "sk_TransformedCoords2D_0.c_str());\n",
             "this->invokeChild(_outer.child_index, &_sample110, args, _coords110.c_str());\n",
             "fragBuilder->codeAppendf(\"%s = %s + %s;\\n\", args.fOutputColor, _sample94.c_str(), "
                                      "_sample110.c_str());\n"
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 27a48ba..cea6949 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -1316,6 +1316,16 @@
          "}\n");
 }
 
+DEF_TEST(SkSLSampleMask, r) {
+    test(r,
+         "void main() { sk_SampleMask[0] |= 8; }",
+         *SkSL::ShaderCapsFactory::SampleMaskSupport(),
+         "#version 400\n"
+         "void main() {\n"
+         "    gl_SampleMask[0] |= 8;\n"
+         "}\n");
+}
+
 DEF_TEST(SkSLVertexID, r) {
     test(r,
          "out int id; void main() { id = sk_VertexID; }",
diff --git a/tests/SkVMTest.cpp b/tests/SkVMTest.cpp
index 7434864..501450c 100644
--- a/tests/SkVMTest.cpp
+++ b/tests/SkVMTest.cpp
@@ -7,7 +7,7 @@
 
 #include "include/core/SkColorPriv.h"
 #include "include/private/SkColorData.h"
-#include "src/core/SkCpu.h"
+#include "src/core/SkMSAN.h"
 #include "src/core/SkVM.h"
 #include "tests/Test.h"
 #include "tools/Resources.h"
@@ -34,29 +34,26 @@
 // TODO: I'd like this to go away and have every test in here run both JIT and interpreter.
 template <typename Fn>
 static void test_interpreter_only(skiatest::Reporter* r, skvm::Program&& program, Fn&& test) {
-#if defined(SKVM_JIT)
     REPORTER_ASSERT(r, !program.hasJIT());
-#endif
     test((const skvm::Program&) program);
 }
 
 template <typename Fn>
 static void test_jit_and_interpreter(skiatest::Reporter* r, skvm::Program&& program, Fn&& test) {
-#if defined(SKVM_JIT)
-    const bool expect_jit
-    #if defined(SK_CPU_X86)
-        = SkCpu::Supports(SkCpu::HSW);
-    #elif defined(SK_CPU_ARM64)
-        = true;
-    #else
-        = false;
-    #endif
-    if (expect_jit) {
+    static const bool can_jit = []{
+        // This is about the simplest program we can write, setting an int buffer to a constant.
+        // If this can't JIT, the platform does not support JITing.
+        skvm::Builder b;
+        b.store32(b.varying<int>(), b.splat(42));
+        skvm::Program p = b.done();
+        return p.hasJIT();
+    }();
+
+    if (can_jit) {
         REPORTER_ASSERT(r, program.hasJIT());
         test((const skvm::Program&) program);
         program.dropJIT();
     }
-#endif
     test_interpreter_only(r, std::move(program), std::move(test));
 }
 
@@ -164,6 +161,7 @@
         dump(b, &buf);
     }
 
+#if defined(SK_CPU_X86)
     sk_sp<SkData> blob = buf.detachAsData();
     {
 
@@ -184,6 +182,7 @@
             }
         }
     }
+#endif
 
     auto test_8888 = [&](skvm::Program&& program) {
         uint32_t src[9];
@@ -469,7 +468,7 @@
         b.store32(b.varying<int>(), m);
     }
 
-    test_interpreter_only(r, b.done(), [&](const skvm::Program& program) {
+    test_jit_and_interpreter(r, b.done(), [&](const skvm::Program& program) {
         float in[] = { 0,1,2,3,4,5,6,7,8,9 };
         int out[SK_ARRAY_COUNT(in)];
 
@@ -563,7 +562,7 @@
                   z = b.mad(y,y,x),   // y is needed in the future, but r[z] = r[x] is ok.
                   w = b.mad(z,z,y),   // w can alias z but not y.
                   v = b.mad(w,y,w);   // Got to stop somewhere.
-        b.store32(arg, b.to_i32(v));
+        b.store32(arg, b.trunc(v));
     }
 
     test_jit_and_interpreter(r, b.done(), [&](const skvm::Program& program) {
@@ -707,6 +706,35 @@
     });
 }
 
+DEF_TEST(SkVM_MSAN, r) {
+    // This little memset32() program should be able to JIT, but if we run that
+    // JIT code in an MSAN build, it won't see the writes initialize buf.  So
+    // this tests that we're using the interpreter instead.
+    skvm::Builder b;
+    b.store32(b.varying<int>(), b.splat(42));
+
+    test_jit_and_interpreter(r, b.done(), [&](const skvm::Program& program) {
+        constexpr int K = 17;
+        int buf[K];                 // Intentionally uninitialized.
+        program.eval(K, buf);
+        sk_msan_assert_initialized(buf, buf+K);
+        for (int x : buf) {
+            REPORTER_ASSERT(r, x == 42);
+        }
+    });
+}
+
+DEF_TEST(SkVM_assert, r) {
+    skvm::Builder b;
+    b.assert_true(b.lt(b.load32(b.varying<int>()),
+                       b.splat(42)));
+
+    test_jit_and_interpreter(r, b.done(), [&](const skvm::Program& program) {
+        int buf[] = { 0,1,2,3,4,5,6,7,8,9 };
+        program.eval(SK_ARRAY_COUNT(buf), buf);
+    });
+}
+
 
 template <typename Fn>
 static void test_asm(skiatest::Reporter* r, Fn&& fn, std::initializer_list<uint8_t> expected) {
@@ -737,9 +765,11 @@
     using A = skvm::Assembler;
     // Our exit strategy from AVX code.
     test_asm(r, [&](A& a) {
+        a.int3();
         a.vzeroupper();
         a.ret();
     },{
+        0xcc,
         0xc5, 0xf8, 0x77,
         0xc3,
     });
@@ -798,11 +828,27 @@
     });
 
     test_asm(r, [&](A& a) {
-        a.vpcmpeqd(A::ymm0, A::ymm1, A::ymm2);
-        a.vpcmpgtd(A::ymm0, A::ymm1, A::ymm2);
+        a.vpcmpeqd (A::ymm0, A::ymm1, A::ymm2);
+        a.vpcmpgtd (A::ymm0, A::ymm1, A::ymm2);
+        a.vcmpeqps (A::ymm0, A::ymm1, A::ymm2);
+        a.vcmpltps (A::ymm0, A::ymm1, A::ymm2);
+        a.vcmpleps (A::ymm0, A::ymm1, A::ymm2);
+        a.vcmpneqps(A::ymm0, A::ymm1, A::ymm2);
     },{
         0xc5,0xf5,0x76,0xc2,
         0xc5,0xf5,0x66,0xc2,
+        0xc5,0xf4,0xc2,0xc2,0x00,
+        0xc5,0xf4,0xc2,0xc2,0x01,
+        0xc5,0xf4,0xc2,0xc2,0x02,
+        0xc5,0xf4,0xc2,0xc2,0x04,
+    });
+
+    test_asm(r, [&](A& a) {
+        a.vminps(A::ymm0, A::ymm1, A::ymm2);
+        a.vmaxps(A::ymm0, A::ymm1, A::ymm2);
+    },{
+        0xc5,0xf4,0x5d,0xc2,
+        0xc5,0xf4,0x5f,0xc2,
     });
 
     test_asm(r, [&](A& a) {
@@ -838,6 +884,12 @@
         a.vbroadcastss(A::ymm15, &l);
 
         a.vpshufb(A::ymm4, A::ymm3, &l);
+        a.vpaddd (A::ymm4, A::ymm3, &l);
+        a.vpsubd (A::ymm4, A::ymm3, &l);
+
+        a.vptest(A::ymm4, &l);
+
+        a.vmulps (A::ymm4, A::ymm3, &l);
     },{
         0x01, 0x02, 0x03, 0x4,
 
@@ -848,6 +900,13 @@
         0xc4, 0x62, 0x7d,  0x18,   0b00'111'101,   0xd8,0xff,0xff,0xff,   // 0xffffffd8 == -40
 
         0xc4, 0xe2, 0x65,  0x00,   0b00'100'101,   0xcf,0xff,0xff,0xff,   // 0xffffffcf == -49
+
+        0xc5, 0xe5,        0xfe,   0b00'100'101,   0xc7,0xff,0xff,0xff,   // 0xffffffc7 == -57
+        0xc5, 0xe5,        0xfa,   0b00'100'101,   0xbf,0xff,0xff,0xff,   // 0xffffffbf == -65
+
+        0xc4, 0xe2, 0x7d,  0x17,   0b00'100'101,   0xb6,0xff,0xff,0xff,   // 0xffffffb6 == -74
+
+        0xc5, 0xe4,        0x59,   0b00'100'101,   0xae,0xff,0xff,0xff,   // 0xffffffaf == -82
     });
 
     test_asm(r, [&](A& a) {
@@ -876,6 +935,7 @@
         a.je (&l);
         a.jmp(&l);
         a.jl (&l);
+        a.jc (&l);
 
         a.cmp(A::rdx, 0);
         a.cmp(A::rax, 12);
@@ -886,6 +946,7 @@
         0x0f,0x84, 0xee,0xff,0xff,0xff,   // near je  -18 bytes
         0xe9,      0xe9,0xff,0xff,0xff,   // near jmp -23 bytes
         0x0f,0x8c, 0xe3,0xff,0xff,0xff,   // near jl  -29 bytes
+        0x0f,0x82, 0xdd,0xff,0xff,0xff,   // near jc  -35 bytes
 
         0x48,0x83,0xfa,0x00,
         0x48,0x83,0xf8,0x0c,
@@ -1005,10 +1066,12 @@
         a.vmovdqa   (A::ymm3, A::ymm2);
         a.vcvttps2dq(A::ymm3, A::ymm2);
         a.vcvtdq2ps (A::ymm3, A::ymm2);
+        a.vcvtps2dq (A::ymm3, A::ymm2);
     },{
         0xc5,0xfd,0x6f,0xda,
         0xc5,0xfe,0x5b,0xda,
         0xc5,0xfc,0x5b,0xda,
+        0xc5,0xfd,0x5b,0xda,
     });
 
     // echo "fmul v4.4s, v3.4s, v1.4s" | llvm-mc -show-encoding -arch arm64
@@ -1019,6 +1082,7 @@
         a.eor16b(A::v4, A::v3, A::v1);
         a.bic16b(A::v4, A::v3, A::v1);
         a.bsl16b(A::v4, A::v3, A::v1);
+        a.not16b(A::v4, A::v3);
 
         a.add4s(A::v4, A::v3, A::v1);
         a.sub4s(A::v4, A::v3, A::v1);
@@ -1034,14 +1098,21 @@
         a.fsub4s(A::v4, A::v3, A::v1);
         a.fmul4s(A::v4, A::v3, A::v1);
         a.fdiv4s(A::v4, A::v3, A::v1);
+        a.fmin4s(A::v4, A::v3, A::v1);
+        a.fmax4s(A::v4, A::v3, A::v1);
 
         a.fmla4s(A::v4, A::v3, A::v1);
+
+        a.fcmeq4s(A::v4, A::v3, A::v1);
+        a.fcmgt4s(A::v4, A::v3, A::v1);
+        a.fcmge4s(A::v4, A::v3, A::v1);
     },{
         0x64,0x1c,0x21,0x4e,
         0x64,0x1c,0xa1,0x4e,
         0x64,0x1c,0x21,0x6e,
         0x64,0x1c,0x61,0x4e,
         0x64,0x1c,0x61,0x6e,
+        0x64,0x58,0x20,0x6e,
 
         0x64,0x84,0xa1,0x4e,
         0x64,0x84,0xa1,0x6e,
@@ -1057,8 +1128,14 @@
         0x64,0xd4,0xa1,0x4e,
         0x64,0xdc,0x21,0x6e,
         0x64,0xfc,0x21,0x6e,
+        0x64,0xf4,0xa1,0x4e,
+        0x64,0xf4,0x21,0x4e,
 
         0x64,0xcc,0x21,0x4e,
+
+        0x64,0xe4,0x21,0x4e,
+        0x64,0xe4,0xa1,0x6e,
+        0x64,0xe4,0x21,0x6e,
     });
 
     test_asm(r, [&](A& a) {
@@ -1116,12 +1193,17 @@
     test_asm(r, [&](A& a) {
         a.scvtf4s (A::v4, A::v3);
         a.fcvtzs4s(A::v4, A::v3);
+        a.fcvtns4s(A::v4, A::v3);
     },{
         0x64,0xd8,0x21,0x4e,
         0x64,0xb8,0xa1,0x4e,
+        0x64,0xa8,0x21,0x4e,
     });
 
     test_asm(r, [&](A& a) {
+        a.brk(0);
+        a.brk(65535);
+
         a.ret(A::x30);   // Conventional ret using link register.
         a.ret(A::x13);   // Can really return using any register if we like.
 
@@ -1145,6 +1227,9 @@
         a.cbnz(A::x2, &l);
         a.cbz(A::x2, &l);
     },{
+        0x00,0x00,0x20,0xd4,
+        0xe0,0xff,0x3f,0xd4,
+
         0xc0,0x03,0x5f,0xd6,
         0xa0,0x01,0x5f,0xd6,
 
@@ -1237,6 +1322,9 @@
         a.ldrs   (A::v0, A::x0);
         a.uxtlb2h(A::v0, A::v0);
         a.uxtlh2s(A::v0, A::v0);
+
+        a.uminv4s(A::v3, A::v4);
+        a.fmovs  (A::x3, A::v4);  // fmov w3,s4
     },{
         0x00,0x28,0x61,0x0e,
         0x00,0x28,0x21,0x0e,
@@ -1245,6 +1333,9 @@
         0x00,0x00,0x40,0xbd,
         0x00,0xa4,0x08,0x2f,
         0x00,0xa4,0x10,0x2f,
+
+        0x83,0xa8,0xb1,0x6e,
+        0x83,0x00,0x26,0x1e,
     });
 
     test_asm(r, [&](A& a) {
diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp
index 68be50c..a8dfc0e 100644
--- a/tests/StringTest.cpp
+++ b/tests/StringTest.cpp
@@ -10,12 +10,9 @@
 #include "include/core/SkString.h"
 #include "src/core/SkStringUtils.h"
 
-#include <stdarg.h>
 #include <stdio.h>
 #include <thread>
 
-static const char* gThirtyWideDecimal = "%30d";
-
 DEF_TEST(String, reporter) {
     SkString    a;
     SkString    b((size_t)0);
@@ -166,18 +163,6 @@
 
     REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0"));
 
-    char buffer [40];
-    memset(buffer, 'a', 40);
-    REPORTER_ASSERT(reporter, buffer[18] == 'a');
-    REPORTER_ASSERT(reporter, buffer[19] == 'a');
-    REPORTER_ASSERT(reporter, buffer[20] == 'a');
-    snprintf(buffer, 20, gThirtyWideDecimal, 0);
-    REPORTER_ASSERT(reporter, buffer[18] == ' ');
-    REPORTER_ASSERT(reporter, buffer[19] == 0);
-    REPORTER_ASSERT(reporter, buffer[20] == 'a');
-
-    REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0"));
-
     // 2000 is larger than the static buffer size inside SkString.cpp
     a = SkStringPrintf("%2000s", " ");
     REPORTER_ASSERT(reporter, a.size() == 2000);
diff --git a/tests/StrokeTest.cpp b/tests/StrokeTest.cpp
index e13a775..84cc5e1 100644
--- a/tests/StrokeTest.cpp
+++ b/tests/StrokeTest.cpp
@@ -168,7 +168,6 @@
     paint.setStrokeWidth(1.49679073e+10f);
 
     SkPath path;
-    path.setFillType(SkPath::kWinding_FillType);
     path.moveTo(SkBits2Float(0x46380000), SkBits2Float(0xc6380000));  // 11776, -11776
     path.lineTo(SkBits2Float(0x46a00000), SkBits2Float(0xc6a00000));  // 20480, -20480
     path.lineTo(SkBits2Float(0x468c0000), SkBits2Float(0xc68c0000));  // 17920, -17920
diff --git a/tests/SurfaceSemaphoreTest.cpp b/tests/SurfaceSemaphoreTest.cpp
index 9dd4515..5e7fc05 100644
--- a/tests/SurfaceSemaphoreTest.cpp
+++ b/tests/SurfaceSemaphoreTest.cpp
@@ -16,8 +16,10 @@
 #include "include/gpu/GrBackendSemaphore.h"
 #include "include/gpu/GrBackendSurface.h"
 
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLGpu.h"
 #include "src/gpu/gl/GrGLUtil.h"
+#endif
 
 #ifdef SK_VULKAN
 #include "include/gpu/vk/GrVkTypes.h"
@@ -134,7 +136,6 @@
     if (GrBackendApi::kVulkan == mainInfo.backend()) {
         // Initialize the secondary semaphore instead of having Ganesh create one internally
         GrVkGpu* gpu = static_cast<GrVkGpu*>(mainCtx->priv().getGpu());
-        const GrVkInterface* interface = gpu->vkInterface();
         VkDevice device = gpu->device();
 
         VkSemaphore vkSem;
@@ -143,7 +144,7 @@
         createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
         createInfo.pNext = nullptr;
         createInfo.flags = 0;
-        GR_VK_CALL_ERRCHECK(interface, CreateSemaphore(device, &createInfo, nullptr, &vkSem));
+        GR_VK_CALL_ERRCHECK(gpu, CreateSemaphore(device, &createInfo, nullptr, &vkSem));
 
         semaphores[1].initVulkan(vkSem);
     }
@@ -183,6 +184,7 @@
     draw_child(reporter, childInfo2, backendTexture, semaphores[1]);
 }
 
+#ifdef SK_GL
 DEF_GPUTEST(SurfaceSemaphores, reporter, options) {
 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
     static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType;
@@ -223,6 +225,7 @@
         }
     }
 }
+#endif
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest, reporter, ctxInfo) {
     GrContext* ctx = ctxInfo.grContext();
@@ -244,6 +247,7 @@
     GrSemaphoresSubmitted submitted = mainSurface->flushAndSignalSemaphores(1, &semaphore);
     REPORTER_ASSERT(reporter, GrSemaphoresSubmitted::kYes == submitted);
 
+#ifdef SK_GL
     if (GrBackendApi::kOpenGL == ctxInfo.backend()) {
         GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->priv().getGpu());
         const GrGLInterface* interface = gpu->glInterface();
@@ -253,6 +257,7 @@
         GR_GL_CALL_RET(interface, result, IsSync(sync));
         REPORTER_ASSERT(reporter, result);
     }
+#endif
 
 #ifdef SK_VULKAN
     if (GrBackendApi::kVulkan == ctxInfo.backend()) {
@@ -284,8 +289,8 @@
         cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
         cmdBufferBeginInfo.pInheritanceInfo = nullptr;
 
-        GR_VK_CALL_ERRCHECK(interface, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
-        GR_VK_CALL_ERRCHECK(interface, EndCommandBuffer(cmdBuffer));
+        GR_VK_CALL_ERRCHECK(gpu, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
+        GR_VK_CALL_ERRCHECK(gpu, EndCommandBuffer(cmdBuffer));
 
         VkFenceCreateInfo fenceInfo;
         VkFence fence;
@@ -308,7 +313,7 @@
         submitInfo.pCommandBuffers = &cmdBuffer;
         submitInfo.signalSemaphoreCount = 0;
         submitInfo.pSignalSemaphores = nullptr;
-        GR_VK_CALL_ERRCHECK(interface, QueueSubmit(queue, 1, &submitInfo, fence));
+        GR_VK_CALL_ERRCHECK(gpu, QueueSubmit(queue, 1, &submitInfo, fence));
 
         err = GR_VK_CALL(interface, WaitForFences(device, 1, &fence, true, 3000000000));
 
diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp
index f9996dc..f7c692d 100644
--- a/tests/TestUtils.cpp
+++ b/tests/TestUtils.cpp
@@ -81,9 +81,8 @@
                          GrColorType colorType,
                          uint32_t expectedPixelValues[],
                          const char* testName) {
-    sk_sp<GrTextureProxy> dstProxy = GrSurfaceProxy::Copy(context, proxy, colorType,
-                                                          GrMipMapped::kNo, SkBackingFit::kExact,
-                                                          SkBudgeted::kYes);
+    sk_sp<GrTextureProxy> dstProxy = GrSurfaceProxy::Copy(context, proxy, GrMipMapped::kNo,
+                                                          SkBackingFit::kExact, SkBudgeted::kYes);
     SkASSERT(dstProxy);
 
     auto dstContext = context->priv().makeWrappedSurfaceContext(std::move(dstProxy), colorType,
diff --git a/tests/TextureBindingsResetTest.cpp b/tests/TextureBindingsResetTest.cpp
index 04602ff..1703a34 100644
--- a/tests/TextureBindingsResetTest.cpp
+++ b/tests/TextureBindingsResetTest.cpp
@@ -12,6 +12,8 @@
 #include "src/gpu/gl/GrGLUtil.h"
 #include "tests/Test.h"
 
+#ifdef SK_GL
+
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(TextureBindingsResetTest, reporter, ctxInfo) {
 #define GL(F) GR_GL_CALL(ctxInfo.glContext()->gl(), F)
 
@@ -164,3 +166,5 @@
 
 #undef GL
 }
+
+#endif  // SK_GL
diff --git a/tests/TraceMemoryDumpTest.cpp b/tests/TraceMemoryDumpTest.cpp
index 16096a2..42e2c47 100644
--- a/tests/TraceMemoryDumpTest.cpp
+++ b/tests/TraceMemoryDumpTest.cpp
@@ -12,9 +12,11 @@
 #include "include/gpu/GrTexture.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrRenderTarget.h"
+#ifdef SK_GL
 #include "src/gpu/gl/GrGLBuffer.h"
 #include "src/gpu/gl/GrGLDefines.h"
 #include "src/gpu/gl/GrGLGpu.h"
+#endif
 
 /*
  * Build test for SkTraceMemoryDump.
@@ -71,6 +73,7 @@
     }
 }
 
+#ifdef SK_GL
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SkTraceMemoryDump_ownedGLBuffer, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
     GrGLGpu* gpu = static_cast<GrGLGpu*>(context->priv().getGpu());
@@ -157,3 +160,4 @@
 
     ValidateMemoryDumps(reporter, context, rt->gpuMemorySize(), false /* isOwned */);
 }
+#endif  // SK_GL
diff --git a/tests/TransferPixelsTest.cpp b/tests/TransferPixelsTest.cpp
index 10e29c7..cb796a0 100644
--- a/tests/TransferPixelsTest.cpp
+++ b/tests/TransferPixelsTest.cpp
@@ -432,7 +432,7 @@
                      GrColorType::kRG_88,
                      GrColorType::kBGRA_8888,
                      GrColorType::kRGBA_1010102,
-                     //  GrColorType::kGray_8, Reading back to kGray is busted.
+                     GrColorType::kGray_8,
                      GrColorType::kAlpha_F16,
                      GrColorType::kRGBA_F16,
                      GrColorType::kRGBA_F16_Clamped,
@@ -454,25 +454,25 @@
     }
     for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
         for (auto colorType : {
-                GrColorType::kAlpha_8,
-                GrColorType::kAlpha_16,
-                GrColorType::kBGR_565,
-                GrColorType::kABGR_4444,
-                GrColorType::kRGBA_8888,
-                GrColorType::kRGBA_8888_SRGB,
-                //  GrColorType::kRGB_888x, Broken in GL until we have kRGB_888
-                GrColorType::kRG_88,
-                GrColorType::kBGRA_8888,
-                GrColorType::kRGBA_1010102,
-                //  GrColorType::kGray_8, Reading back to kGray is busted.
-                GrColorType::kAlpha_F16,
-                GrColorType::kRGBA_F16,
-                GrColorType::kRGBA_F16_Clamped,
-                GrColorType::kRGBA_F32,
-                GrColorType::kRG_1616,
-                GrColorType::kRGBA_16161616,
-                GrColorType::kRG_F16,
-        }) {
+                     GrColorType::kAlpha_8,
+                     GrColorType::kAlpha_16,
+                     GrColorType::kBGR_565,
+                     GrColorType::kABGR_4444,
+                     GrColorType::kRGBA_8888,
+                     GrColorType::kRGBA_8888_SRGB,
+                     //  GrColorType::kRGB_888x, Broken in GL until we have kRGB_888
+                     GrColorType::kRG_88,
+                     GrColorType::kBGRA_8888,
+                     GrColorType::kRGBA_1010102,
+                     GrColorType::kGray_8,
+                     GrColorType::kAlpha_F16,
+                     GrColorType::kRGBA_F16,
+                     GrColorType::kRGBA_F16_Clamped,
+                     GrColorType::kRGBA_F32,
+                     GrColorType::kRG_1616,
+                     GrColorType::kRGBA_16161616,
+                     GrColorType::kRG_F16,
+             }) {
             basic_transfer_from_test(reporter, ctxInfo, colorType, renderable);
         }
     }
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
index 294334d..f2ed05d 100644
--- a/tests/UtilsTest.cpp
+++ b/tests/UtilsTest.cpp
@@ -228,9 +228,7 @@
     auto enumeration = SkMakeEnumerate(A);
 
     size_t check = 0;
-    for (auto t : enumeration) {
-        size_t i; int v;
-        std::tie(i, v) = t;
+    for (auto [i, v] : enumeration) {
         REPORTER_ASSERT(reporter, i == check);
         REPORTER_ASSERT(reporter, v == (int)check+1);
 
@@ -238,9 +236,7 @@
     }
 
     check = 0;
-    for (auto t : SkMakeEnumerate(A)) {
-        size_t i; int v;
-        std::tie(i, v) = t;
+    for (auto [i, v] : SkMakeEnumerate(A)) {
         REPORTER_ASSERT(reporter, i == check);
         REPORTER_ASSERT(reporter, v == (int)check+1);
 
@@ -249,9 +245,7 @@
 
     check = 0;
     std::vector<int> vec = {1, 2, 3, 4};
-    for (auto t : SkMakeEnumerate(vec)) {
-        size_t i; int v;
-        std::tie(i, v) = t;
+    for (auto [i, v] : SkMakeEnumerate(vec)) {
         REPORTER_ASSERT(reporter, i == check);
         REPORTER_ASSERT(reporter, v == (int)check+1);
         check++;
@@ -259,9 +253,7 @@
     REPORTER_ASSERT(reporter, check == 4);
 
     check = 0;
-    for (auto t : SkMakeEnumerate(SkMakeSpan(vec))) {
-        size_t i; int v;
-        std::tie(i, v) = t;
+    for (auto [i, v] : SkMakeEnumerate(SkMakeSpan(vec))) {
         REPORTER_ASSERT(reporter, i == check);
         REPORTER_ASSERT(reporter, v == (int)check+1);
         check++;
@@ -302,9 +294,7 @@
     {
         // Check ranged-for
         int i = 0;
-        for (auto t : z) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b, c, d, s) = t;
+        for (auto [a, b, c, d, s] : z) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -319,9 +309,7 @@
     {
         // Check first(n)
         int i = 0;
-        for (auto t : z.first(2)) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b, c, d, s) = t;
+        for (auto [a, b, c, d, s] : z.first(2)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -333,14 +321,45 @@
         REPORTER_ASSERT(reporter, i = 2);
     }
 
+    {
+        // Check last(n)
+        int i = 0;
+        for (auto t : z.last(2)) {
+            uint16_t a; float b; int c; int d; int s;
+            std::tie(a, b, c, d, s) = t;
+            REPORTER_ASSERT(reporter, a == A[i + 2]);
+            REPORTER_ASSERT(reporter, b == B[i + 2]);
+            REPORTER_ASSERT(reporter, c == C[i + 2]);
+            REPORTER_ASSERT(reporter, d == D[i + 2]);
+            REPORTER_ASSERT(reporter, s == S[i + 2]);
+
+            i++;
+        }
+        REPORTER_ASSERT(reporter, i = 2);
+    }
+
+    {
+        // Check subspan(offset, count)
+        int i = 0;
+        for (auto t : z.subspan(1, 2)) {
+            uint16_t a; float b; int c; int d; int s;
+            std::tie(a, b, c, d, s) = t;
+            REPORTER_ASSERT(reporter, a == A[i + 1]);
+            REPORTER_ASSERT(reporter, b == B[i + 1]);
+            REPORTER_ASSERT(reporter, c == C[i + 1]);
+            REPORTER_ASSERT(reporter, d == D[i + 1]);
+            REPORTER_ASSERT(reporter, s == S[i + 1]);
+
+            i++;
+        }
+        REPORTER_ASSERT(reporter, i = 2);
+    }
 
     {
         // Check copy.
         auto zz{z};
         int i = 0;
-        for (auto t : zz) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b, c, d, s) = t;
+        for (auto [a, b, c, d, s] : zz) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -356,9 +375,7 @@
         // Check const restricting copy
         SkZip<const uint16_t, const float, const int, int, int> cz = z;
         int i = 0;
-        for (auto t : cz) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b, c, d, s) = t;
+        for (auto [a, b, c, d, s] : cz) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -386,26 +403,26 @@
     // The following mutates the data.
     {
         // Check indexing
-        auto t = z[1];
-        REPORTER_ASSERT(reporter, std::get<0>(t) == 2);
-        REPORTER_ASSERT(reporter, std::get<1>(t) == 20.f);
-        REPORTER_ASSERT(reporter, std::get<2>(t) == 30);
-        REPORTER_ASSERT(reporter, std::get<3>(t) == 200);
-        REPORTER_ASSERT(reporter, std::get<4>(t) == 30);
+        auto [a, b, c, d, e] = z[1];
+        REPORTER_ASSERT(reporter, a == 2);
+        REPORTER_ASSERT(reporter, b == 20.f);
+        REPORTER_ASSERT(reporter, c == 30);
+        REPORTER_ASSERT(reporter, d == 200);
+        REPORTER_ASSERT(reporter, e == 30);
 
         // Check correct refs returned.
-        REPORTER_ASSERT(reporter, &std::get<0>(t) == &A[1]);
-        REPORTER_ASSERT(reporter, &std::get<1>(t) == &B[1]);
-        REPORTER_ASSERT(reporter, &std::get<2>(t) == &C[1]);
-        REPORTER_ASSERT(reporter, &std::get<3>(t) == &D[1]);
-        REPORTER_ASSERT(reporter, &std::get<4>(t) == &S[1]);
+        REPORTER_ASSERT(reporter, &a == &A[1]);
+        REPORTER_ASSERT(reporter, &b == &B[1]);
+        REPORTER_ASSERT(reporter, &c == &C[1]);
+        REPORTER_ASSERT(reporter, &d == &D[1]);
+        REPORTER_ASSERT(reporter, &e == &S[1]);
 
         // Check assignment
-        std::get<0>(t) = 20;
+        a = 20;
         // std::get<1>(t) = 300.f; // is const
-        std::get<2>(t) = 300;
-        std::get<3>(t) = 2000;
-        std::get<4>(t) = 300;
+        c = 300;
+        d = 2000;
+        e = 300;
 
         auto t1 = z[1];
         REPORTER_ASSERT(reporter, std::get<0>(t1) == 20);
@@ -428,9 +445,7 @@
         auto zz = SkMakeZip(&A[0], B, C, D, S, P);
 
         int i = 0;
-        for (auto t : zz) {
-            uint16_t a; float b; int c; int d; int s; uint16_t p;
-            std::tie(a, b ,c ,d, s, p) = t;
+        for (auto [a, b, c, d, s, p] : zz) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -446,9 +461,7 @@
     {
         // Check SkMakeZip in ranged for check OneSize calc of B.
         int i = 0;
-        for (auto t : SkMakeZip(&A[0], B, C, D, S)) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b ,c ,d, s) = t;
+        for (auto [a, b, c, d, s] : SkMakeZip(&A[0], B, C, D, S)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -463,9 +476,7 @@
     {
         // Check SkMakeZip in ranged for OneSize of C
         int i = 0;
-        for (auto t : SkMakeZip(&A[0], &B[0], C, D, S)) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(a, b ,c ,d, s) = t;
+        for (auto [a, b, c, d, s] : SkMakeZip(&A[0], &B[0], C, D, S)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -480,9 +491,7 @@
     {
         // Check SkMakeZip in ranged for OneSize for S
         int i = 0;
-        for (auto t : SkMakeZip(S, A, B, C, D)) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(s, a, b, c, d) = t;
+        for (auto [s, a, b, c, d] : SkMakeZip(S, A, B, C, D)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -497,9 +506,7 @@
     {
         // Check SkMakeZip in ranged for
         int i = 0;
-        for (auto t : SkMakeZip(C, S, A, B, D)) {
-            uint16_t a; float b; int c; int d; int s;
-            std::tie(c, s, a, b, d) = t;
+        for (auto [c, s, a, b, d] : SkMakeZip(C, S, A, B, D)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -514,10 +521,7 @@
     {
         // Check SkEnumerate and SkMakeZip in ranged for
         auto zz = SkMakeZip(A, B, C, D, S);
-        for (auto t : SkMakeEnumerate(zz)) {
-            int i;
-            uint16_t a; float b; int c; int d; int s;
-            std::forward_as_tuple(i, std::tie(a, b, c, d, s)) = t;
+        for (auto [i, a, b, c, d, s] : SkMakeEnumerate(zz)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -529,10 +533,7 @@
     {
         // Check SkEnumerate and SkMakeZip in ranged for
         const auto& zz = SkMakeZip(A, B, C, D, S);
-        for (auto t : SkMakeEnumerate(zz)) {
-            int i;
-            uint16_t a; float b; int c; int d; int s;
-            std::forward_as_tuple(i, std::tie(a, b, c, d, s)) = t;
+        for (auto [i, a, b, c, d, s] : SkMakeEnumerate(zz)) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
@@ -543,10 +544,7 @@
 
     {
         // Check SkEnumerate and SkMakeZip in ranged for
-        for (auto t : SkMakeEnumerate(SkMakeZip(A, B, C, D, S))) {
-            int i;
-            uint16_t a; float b; int c; int d; int s;
-            std::forward_as_tuple(i, std::tie(a, b, c, d, s)) = t;
+        for (auto [i, a, b, c, d, s] : SkMakeEnumerate(SkMakeZip(A, B, C, D, S))) {
             REPORTER_ASSERT(reporter, a == A[i]);
             REPORTER_ASSERT(reporter, b == B[i]);
             REPORTER_ASSERT(reporter, c == C[i]);
diff --git a/tests/VkHardwareBufferTest.cpp b/tests/VkHardwareBufferTest.cpp
index 07563cb..cfe453b 100644
--- a/tests/VkHardwareBufferTest.cpp
+++ b/tests/VkHardwareBufferTest.cpp
@@ -70,6 +70,7 @@
     int fFdHandle = 0;
 };
 
+#ifdef SK_GL
 class EGLTestHelper : public BaseTestHelper {
 public:
     EGLTestHelper(const GrContextOptions& options) : fFactory(options) {}
@@ -362,6 +363,7 @@
     fenceSync->waitFence(fence);
     fenceSync->deleteFence(fence);
 }
+#endif  // SK_GL
 
 #define DECLARE_VK_PROC(name) PFN_vk##name fVk##name
 
@@ -1078,7 +1080,11 @@
     if (SrcType::kVulkan == srcType) {
         srcHelper.reset(new VulkanTestHelper());
     } else if (SrcType::kEGL == srcType) {
+#ifdef SK_GL
         srcHelper.reset(new EGLTestHelper(options));
+#else
+        SkASSERT(false, "SrcType::kEGL used without OpenGL support.");
+#endif
     }
     if (srcHelper) {
         if (!srcHelper->init(reporter)) {
@@ -1090,8 +1096,12 @@
     if (DstType::kVulkan == dstType) {
         dstHelper.reset(new VulkanTestHelper());
     } else {
+#ifdef SK_GL
         SkASSERT(DstType::kEGL == dstType);
         dstHelper.reset(new EGLTestHelper(options));
+#else
+        SkASSERT(false, "DstType::kEGL used without OpenGL support.");
+#endif
     }
     if (dstHelper) {
         if (!dstHelper->init(reporter)) {
@@ -1275,14 +1285,19 @@
     run_test(reporter, options, SrcType::kCPU, DstType::kVulkan, false);
 }
 
-DEF_GPUTEST(VulkanHardwareBuffer_EGL_Vulkan, reporter, options) {
-    run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, false);
-}
-
 DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan, reporter, options) {
     run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, false);
 }
 
+DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan_Syncs, reporter, options) {
+    run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, true);
+}
+
+#if defined(SK_GL)
+DEF_GPUTEST(VulkanHardwareBuffer_EGL_Vulkan, reporter, options) {
+    run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, false);
+}
+
 DEF_GPUTEST(VulkanHardwareBuffer_CPU_EGL, reporter, options) {
     run_test(reporter, options, SrcType::kCPU, DstType::kEGL, false);
 }
@@ -1306,10 +1321,8 @@
 DEF_GPUTEST(VulkanHardwareBuffer_EGL_Vulkan_Syncs, reporter, options) {
     run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, true);
 }
-
-DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan_Syncs, reporter, options) {
-    run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, true);
-}
-
 #endif
 
+#endif  // SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) &&
+        // __ANDROID_API__ >= 26 && defined(SK_VULKAN)
+
diff --git a/tests/VkProtectedContextTest.cpp b/tests/VkProtectedContextTest.cpp
index 564d132..2283a7f 100644
--- a/tests/VkProtectedContextTest.cpp
+++ b/tests/VkProtectedContextTest.cpp
@@ -250,6 +250,46 @@
         surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess));
 }
 
+namespace {
+
+struct AsyncContext {
+    bool fCalled = false;
+    std::unique_ptr<const SkSurface::AsyncReadResult> fResult;
+};
+
+static void async_callback(void* c, std::unique_ptr<const SkSurface::AsyncReadResult> result) {
+    auto context = static_cast<AsyncContext*>(c);
+    context->fResult = std::move(result);
+    context->fCalled = true;
+};
+
+}  // anonymous namespace
+
+DEF_GPUTEST(VkProtectedContext_AsyncReadFromProtectedSurface, reporter, options) {
+    auto protectedTestHelper = std::make_unique<VulkanTestHelper>(true);
+    if (!protectedTestHelper->init(reporter)) {
+        return;
+    }
+    REPORTER_ASSERT(reporter, protectedTestHelper->grContext() != nullptr);
+
+    auto surface = protectedTestHelper->createSkSurface(reporter);
+    REPORTER_ASSERT(reporter, surface);
+    AsyncContext cbContext;
+    const auto image_info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType,
+                                      SkColorSpace::MakeSRGB());
+    surface->asyncRescaleAndReadPixelsYUV420(kIdentity_SkYUVColorSpace, SkColorSpace::MakeSRGB(),
+                                             image_info.bounds(), image_info.dimensions(),
+                                             SkSurface::RescaleGamma::kSrc, kNone_SkFilterQuality,
+                                             &async_callback, &cbContext);
+    while (!cbContext.fCalled) {
+        surface->getCanvas()->getGrContext()->checkAsyncWorkCompletion();
+    }
+    REPORTER_ASSERT(reporter, !cbContext.fResult);
+
+    protectedTestHelper->grContext()->deleteBackendTexture(
+        surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess));
+}
+
 DEF_GPUTEST(VkProtectedContext_DrawRectangle, reporter, options) {
     auto protectedTestHelper = std::make_unique<VulkanTestHelper>(true);
     if (!protectedTestHelper->init(reporter)) {
diff --git a/tests/YUVTest.cpp b/tests/YUVTest.cpp
index 0180f07..070403a 100644
--- a/tests/YUVTest.cpp
+++ b/tests/YUVTest.cpp
@@ -135,6 +135,7 @@
         kJPEG_SkYUVColorSpace,
         kRec601_SkYUVColorSpace,
         kRec709_SkYUVColorSpace,
+        kBT2020_SkYUVColorSpace,
         kIdentity_SkYUVColorSpace,
     };
 
diff --git a/third_party/dawn/BUILD.gn b/third_party/dawn/BUILD.gn
index bb53dde..5490431 100644
--- a/third_party/dawn/BUILD.gn
+++ b/third_party/dawn/BUILD.gn
@@ -54,8 +54,8 @@
   target = "dawn_native_utils"
   outputs = [
     "src/dawn_native/ProcTable.cpp",
-    "src/dawn_native/dawn_structs_autogen.h",
-    "src/dawn_native/dawn_structs_autogen.cpp",
+    "src/dawn_native/wgpu_structs_autogen.h",
+    "src/dawn_native/wgpu_structs_autogen.cpp",
     "src/dawn_native/ValidationUtils_autogen.h",
     "src/dawn_native/ValidationUtils_autogen.cpp",
   ]
@@ -135,8 +135,13 @@
                            "src/dawn_native/BindGroup.h",
                            "src/dawn_native/BindGroupLayout.cpp",
                            "src/dawn_native/BindGroupLayout.h",
+                           "src/dawn_native/BuddyAllocator.cpp",
+                           "src/dawn_native/BuddyAllocator.h",
+                           "src/dawn_native/BuddyMemoryAllocator.cpp",
+                           "src/dawn_native/BuddyMemoryAllocator.h",
                            "src/dawn_native/Buffer.cpp",
                            "src/dawn_native/Buffer.h",
+                           "src/dawn_native/CachedObject.cpp",
                            "src/dawn_native/CommandAllocator.cpp",
                            "src/dawn_native/CommandAllocator.h",
                            "src/dawn_native/CommandBuffer.cpp",
@@ -207,6 +212,8 @@
                            "src/dawn_native/RenderPassEncoder.h",
                            "src/dawn_native/RenderPipeline.cpp",
                            "src/dawn_native/RenderPipeline.h",
+                           "src/dawn_native/ResourceHeap.h",
+                           "src/dawn_native/ResourceHeapAllocator.h",
                            "src/dawn_native/ResourceMemoryAllocation.cpp",
                            "src/dawn_native/ResourceMemoryAllocation.h",
                            "src/dawn_native/RingBufferAllocator.cpp",
@@ -231,72 +238,72 @@
 
   if (dawn_enable_d3d12) {
     libs += [ "dxguid.lib" ]
-    sources += rebase_path(
-            [
-              "src/dawn_native/d3d12/AdapterD3D12.cpp",
-              "src/dawn_native/d3d12/AdapterD3D12.h",
-              "src/dawn_native/d3d12/BackendD3D12.cpp",
-              "src/dawn_native/d3d12/BackendD3D12.h",
-              "src/dawn_native/d3d12/BindGroupD3D12.cpp",
-              "src/dawn_native/d3d12/BindGroupD3D12.h",
-              "src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp",
-              "src/dawn_native/d3d12/BindGroupLayoutD3D12.h",
-              "src/dawn_native/d3d12/BufferD3D12.cpp",
-              "src/dawn_native/d3d12/BufferD3D12.h",
-              "src/dawn_native/d3d12/CommandAllocatorManager.cpp",
-              "src/dawn_native/d3d12/CommandAllocatorManager.h",
-              "src/dawn_native/d3d12/CommandBufferD3D12.cpp",
-              "src/dawn_native/d3d12/CommandBufferD3D12.h",
-              "src/dawn_native/d3d12/CommandRecordingContext.cpp",
-              "src/dawn_native/d3d12/CommandRecordingContext.h",
-              "src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.cpp",
-              "src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h",
-              "src/dawn_native/d3d12/ComputePipelineD3D12.cpp",
-              "src/dawn_native/d3d12/ComputePipelineD3D12.h",
-              "src/dawn_native/d3d12/D3D12Backend.cpp",
-              "src/dawn_native/d3d12/D3D12Error.cpp",
-              "src/dawn_native/d3d12/D3D12Error.h",
-              "src/dawn_native/d3d12/D3D12Info.cpp",
-              "src/dawn_native/d3d12/D3D12Info.h",
-              "src/dawn_native/d3d12/DescriptorHeapAllocator.cpp",
-              "src/dawn_native/d3d12/DescriptorHeapAllocator.h",
-              "src/dawn_native/d3d12/DeviceD3D12.cpp",
-              "src/dawn_native/d3d12/DeviceD3D12.h",
-              "src/dawn_native/d3d12/Forward.h",
-              "src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp",
-              "src/dawn_native/d3d12/NativeSwapChainImplD3D12.h",
-              "src/dawn_native/d3d12/PipelineLayoutD3D12.cpp",
-              "src/dawn_native/d3d12/PipelineLayoutD3D12.h",
-              "src/dawn_native/d3d12/PlatformFunctions.cpp",
-              "src/dawn_native/d3d12/PlatformFunctions.h",
-              "src/dawn_native/d3d12/QueueD3D12.cpp",
-              "src/dawn_native/d3d12/QueueD3D12.h",
-              "src/dawn_native/d3d12/RenderPipelineD3D12.cpp",
-              "src/dawn_native/d3d12/RenderPipelineD3D12.h",
-              "src/dawn_native/d3d12/ResourceAllocator.cpp",
-              "src/dawn_native/d3d12/ResourceAllocator.h",
-              "src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp",
-              "src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h",
-              "src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp",
-              "src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h",
-              "src/dawn_native/d3d12/SamplerD3D12.cpp",
-              "src/dawn_native/d3d12/SamplerD3D12.h",
-              "src/dawn_native/d3d12/ShaderModuleD3D12.cpp",
-              "src/dawn_native/d3d12/ShaderModuleD3D12.h",
-              "src/dawn_native/d3d12/StagingBufferD3D12.cpp",
-              "src/dawn_native/d3d12/StagingBufferD3D12.h",
-              "src/dawn_native/d3d12/SwapChainD3D12.cpp",
-              "src/dawn_native/d3d12/SwapChainD3D12.h",
-              "src/dawn_native/d3d12/TextureCopySplitter.cpp",
-              "src/dawn_native/d3d12/TextureCopySplitter.h",
-              "src/dawn_native/d3d12/TextureD3D12.cpp",
-              "src/dawn_native/d3d12/TextureD3D12.h",
-              "src/dawn_native/d3d12/UtilsD3D12.cpp",
-              "src/dawn_native/d3d12/UtilsD3D12.h",
-              "src/dawn_native/d3d12/d3d12_platform.h",
-            ],
-            ".",
-            "$dawn_root")
+    sources +=
+        rebase_path([
+                      "src/dawn_native/d3d12/AdapterD3D12.cpp",
+                      "src/dawn_native/d3d12/AdapterD3D12.h",
+                      "src/dawn_native/d3d12/BackendD3D12.cpp",
+                      "src/dawn_native/d3d12/BackendD3D12.h",
+                      "src/dawn_native/d3d12/BindGroupD3D12.cpp",
+                      "src/dawn_native/d3d12/BindGroupD3D12.h",
+                      "src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp",
+                      "src/dawn_native/d3d12/BindGroupLayoutD3D12.h",
+                      "src/dawn_native/d3d12/BufferD3D12.cpp",
+                      "src/dawn_native/d3d12/BufferD3D12.h",
+                      "src/dawn_native/d3d12/CommandAllocatorManager.cpp",
+                      "src/dawn_native/d3d12/CommandAllocatorManager.h",
+                      "src/dawn_native/d3d12/CommandBufferD3D12.cpp",
+                      "src/dawn_native/d3d12/CommandBufferD3D12.h",
+                      "src/dawn_native/d3d12/CommandRecordingContext.cpp",
+                      "src/dawn_native/d3d12/CommandRecordingContext.h",
+                      "src/dawn_native/d3d12/ComputePipelineD3D12.cpp",
+                      "src/dawn_native/d3d12/ComputePipelineD3D12.h",
+                      "src/dawn_native/d3d12/D3D12Backend.cpp",
+                      "src/dawn_native/d3d12/D3D12Error.cpp",
+                      "src/dawn_native/d3d12/D3D12Error.h",
+                      "src/dawn_native/d3d12/D3D12Info.cpp",
+                      "src/dawn_native/d3d12/D3D12Info.h",
+                      "src/dawn_native/d3d12/DescriptorHeapAllocator.cpp",
+                      "src/dawn_native/d3d12/DescriptorHeapAllocator.h",
+                      "src/dawn_native/d3d12/DeviceD3D12.cpp",
+                      "src/dawn_native/d3d12/DeviceD3D12.h",
+                      "src/dawn_native/d3d12/Forward.h",
+                      "src/dawn_native/d3d12/HeapD3D12.cpp",
+                      "src/dawn_native/d3d12/HeapD3D12.h",
+                      "src/dawn_native/d3d12/HeapAllocatorD3D12.cpp",
+                      "src/dawn_native/d3d12/HeapAllocatorD3D12.h",
+                      "src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp",
+                      "src/dawn_native/d3d12/NativeSwapChainImplD3D12.h",
+                      "src/dawn_native/d3d12/PipelineLayoutD3D12.cpp",
+                      "src/dawn_native/d3d12/PipelineLayoutD3D12.h",
+                      "src/dawn_native/d3d12/PlatformFunctions.cpp",
+                      "src/dawn_native/d3d12/PlatformFunctions.h",
+                      "src/dawn_native/d3d12/QueueD3D12.cpp",
+                      "src/dawn_native/d3d12/QueueD3D12.h",
+                      "src/dawn_native/d3d12/RenderPipelineD3D12.cpp",
+                      "src/dawn_native/d3d12/RenderPipelineD3D12.h",
+                      "src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp",
+                      "src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h",
+                      "src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp",
+                      "src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h",
+                      "src/dawn_native/d3d12/SamplerD3D12.cpp",
+                      "src/dawn_native/d3d12/SamplerD3D12.h",
+                      "src/dawn_native/d3d12/ShaderModuleD3D12.cpp",
+                      "src/dawn_native/d3d12/ShaderModuleD3D12.h",
+                      "src/dawn_native/d3d12/StagingBufferD3D12.cpp",
+                      "src/dawn_native/d3d12/StagingBufferD3D12.h",
+                      "src/dawn_native/d3d12/SwapChainD3D12.cpp",
+                      "src/dawn_native/d3d12/SwapChainD3D12.h",
+                      "src/dawn_native/d3d12/TextureCopySplitter.cpp",
+                      "src/dawn_native/d3d12/TextureCopySplitter.h",
+                      "src/dawn_native/d3d12/TextureD3D12.cpp",
+                      "src/dawn_native/d3d12/TextureD3D12.h",
+                      "src/dawn_native/d3d12/UtilsD3D12.cpp",
+                      "src/dawn_native/d3d12/UtilsD3D12.h",
+                      "src/dawn_native/d3d12/d3d12_platform.h",
+                    ],
+                    ".",
+                    "$dawn_root")
   }
 
   if (dawn_enable_metal) {
@@ -421,16 +428,14 @@
               "src/dawn_native/vulkan/CommandBufferVk.h",
               "src/dawn_native/vulkan/ComputePipelineVk.cpp",
               "src/dawn_native/vulkan/ComputePipelineVk.h",
+              "src/dawn_native/vulkan/DescriptorSetService.cpp",
+              "src/dawn_native/vulkan/DescriptorSetService.h",
               "src/dawn_native/vulkan/DeviceVk.cpp",
               "src/dawn_native/vulkan/DeviceVk.h",
               "src/dawn_native/vulkan/ExternalHandle.h",
               "src/dawn_native/vulkan/FencedDeleter.cpp",
               "src/dawn_native/vulkan/FencedDeleter.h",
               "src/dawn_native/vulkan/Forward.h",
-              "src/dawn_native/vulkan/MemoryAllocator.cpp",
-              "src/dawn_native/vulkan/MemoryAllocator.h",
-              "src/dawn_native/vulkan/MemoryResourceAllocatorVk.cpp",
-              "src/dawn_native/vulkan/MemoryResourceAllocatorVk.h",
               "src/dawn_native/vulkan/NativeSwapChainImplVk.cpp",
               "src/dawn_native/vulkan/NativeSwapChainImplVk.h",
               "src/dawn_native/vulkan/PipelineLayoutVk.cpp",
@@ -441,8 +446,10 @@
               "src/dawn_native/vulkan/RenderPassCache.h",
               "src/dawn_native/vulkan/RenderPipelineVk.cpp",
               "src/dawn_native/vulkan/RenderPipelineVk.h",
-              "src/dawn_native/vulkan/ResourceMemoryVk.cpp",
-              "src/dawn_native/vulkan/ResourceMemoryVk.h",
+              "src/dawn_native/vulkan/ResourceHeapVk.cpp",
+              "src/dawn_native/vulkan/ResourceHeapVk.h",
+              "src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp",
+              "src/dawn_native/vulkan/ResourceMemoryAllocatorVk.h",
               "src/dawn_native/vulkan/SamplerVk.cpp",
               "src/dawn_native/vulkan/SamplerVk.h",
               "src/dawn_native/vulkan/ShaderModuleVk.cpp",
@@ -567,6 +574,7 @@
   outputs = [
     "src/include/dawn/dawn.h",
     "src/include/dawn/dawn_proc_table.h",
+    "src/include/dawn/webgpu.h",
   ]
 }
 
@@ -591,6 +599,7 @@
   target = "dawncpp_headers"
   outputs = [
     "src/include/dawn/dawncpp.h",
+    "src/include/dawn/webgpu_cpp.h",
   ]
 }
 
@@ -611,7 +620,7 @@
 dawn_json_generator("dawncpp_gen") {
   target = "dawncpp"
   outputs = [
-    "src/dawn/dawncpp.cpp",
+    "src/dawn/webgpu_cpp.cpp",
   ]
 }
 
diff --git a/third_party/freetype2/BUILD.gn b/third_party/freetype2/BUILD.gn
index b76c2df..426a3fa 100644
--- a/third_party/freetype2/BUILD.gn
+++ b/third_party/freetype2/BUILD.gn
@@ -6,7 +6,7 @@
 declare_args() {
   # TODO: build from source all the time for testing?
   skia_use_system_freetype2 =
-      is_official_build || !(is_android || sanitize == "MSAN")
+      (is_official_build || !(is_android || sanitize == "MSAN")) && !is_fuchsia
 }
 
 import("../third_party.gni")
diff --git a/third_party/gif/LICENSE b/third_party/gif/LICENSE
deleted file mode 100644
index e496845..0000000
--- a/third_party/gif/LICENSE
+++ /dev/null
@@ -1,37 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Chris Saari <saari@netscape.com>
- *   Apple Computer
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
diff --git a/third_party/gif/SkGifImageReader.cpp b/third_party/gif/SkGifImageReader.cpp
deleted file mode 100644
index 23327d9..0000000
--- a/third_party/gif/SkGifImageReader.cpp
+++ /dev/null
@@ -1,958 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Chris Saari <saari@netscape.com>
- *   Apple Computer
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
-The Graphics Interchange Format(c) is the copyright property of CompuServe
-Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
-enhance, alter, modify or change in any way the definition of the format.
-
-CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
-license for the use of the Graphics Interchange Format(sm) in computer
-software; computer software utilizing GIF(sm) must acknowledge ownership of the
-Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
-User and Technical Documentation. Computer software utilizing GIF, which is
-distributed or may be distributed without User or Technical Documentation must
-display to the screen or printer a message acknowledging ownership of the
-Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
-this case, the acknowledgement may be displayed in an opening screen or leading
-banner, or a closing screen or trailing banner. A message such as the following
-may be used:
-
-    "The Graphics Interchange Format(c) is the Copyright property of
-    CompuServe Incorporated. GIF(sm) is a Service Mark property of
-    CompuServe Incorporated."
-
-For further information, please contact :
-
-    CompuServe Incorporated
-    Graphics Technology Department
-    5000 Arlington Center Boulevard
-    Columbus, Ohio  43220
-    U. S. A.
-
-CompuServe Incorporated maintains a mailing list with all those individuals and
-organizations who wish to receive copies of this document when it is corrected
-or revised. This service is offered free of charge; please provide us with your
-mailing address.
-*/
-
-#include "include/core/SkColorPriv.h"
-#include "src/codec/SkGifCodec.h"
-#include "third_party/gif/SkGifImageReader.h"
-
-#include <algorithm>
-#include <string.h>
-
-
-// GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'.
-//
-// Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
-// as each GIF block (except colormaps) can never be bigger than 256 bytes.
-// Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap.
-// So a fixed buffer in SkGifImageReader is good enough.
-// This buffer is only needed to copy left-over data from one GifWrite call to the next
-#define GETN(n, s) \
-    do { \
-        m_bytesToConsume = (n); \
-        m_state = (s); \
-    } while (0)
-
-// Get a 16-bit value stored in little-endian format.
-#define GETINT16(p)   ((p)[1]<<8|(p)[0])
-
-namespace {
-    bool is_palette_index_valid(int transparentIndex) {
-        // -1 is a signal that there is no transparent index.
-        // Otherwise, it is encoded in 8 bits, and all 256 values are considered
-        // valid since a GIF may use an index outside of the palette to be
-        // transparent.
-        return transparentIndex >= 0;
-    }
-} // anonymous namespace
-
-// Send the data to the display front-end.
-void SkGIFLZWContext::outputRow(const unsigned char* rowBegin)
-{
-    int drowStart = irow;
-    int drowEnd = irow;
-
-    // Haeberli-inspired hack for interlaced GIFs: Replicate lines while
-    // displaying to diminish the "venetian-blind" effect as the image is
-    // loaded. Adjust pixel vertical positions to avoid the appearance of the
-    // image crawling up the screen as successive passes are drawn.
-    if (m_frameContext->progressiveDisplay() && m_frameContext->interlaced() && ipass < 4) {
-        unsigned rowDup = 0;
-        unsigned rowShift = 0;
-
-        switch (ipass) {
-        case 1:
-            rowDup = 7;
-            rowShift = 3;
-            break;
-        case 2:
-            rowDup = 3;
-            rowShift = 1;
-            break;
-        case 3:
-            rowDup = 1;
-            rowShift = 0;
-            break;
-        default:
-            break;
-        }
-
-        drowStart -= rowShift;
-        drowEnd = drowStart + rowDup;
-
-        // Extend if bottom edge isn't covered because of the shift upward.
-        if ((unsigned)((m_frameContext->height() - 1) - drowEnd) <= rowShift)
-            drowEnd = m_frameContext->height() - 1;
-
-        // Clamp first and last rows to upper and lower edge of image.
-        if (drowStart < 0)
-            drowStart = 0;
-
-        if (drowEnd >= m_frameContext->height())
-            drowEnd = m_frameContext->height() - 1;
-    }
-
-    // Protect against too much image data.
-    if (drowStart >= m_frameContext->height())
-        return;
-
-    // CALLBACK: Let the client know we have decoded a row.
-    const bool writeTransparentPixels =
-            SkCodec::kNoFrame == m_frameContext->getRequiredFrame();
-    m_client->haveDecodedRow(m_frameContext->frameId(), rowBegin,
-            drowStart, drowEnd - drowStart + 1, writeTransparentPixels);
-
-    if (!m_frameContext->interlaced())
-        irow++;
-    else {
-        do {
-            switch (ipass) {
-            case 1:
-                irow += 8;
-                if (irow >= (unsigned) m_frameContext->height()) {
-                    ipass++;
-                    irow = 4;
-                }
-                break;
-
-            case 2:
-                irow += 8;
-                if (irow >= (unsigned) m_frameContext->height()) {
-                    ipass++;
-                    irow = 2;
-                }
-                break;
-
-            case 3:
-                irow += 4;
-                if (irow >= (unsigned) m_frameContext->height()) {
-                    ipass++;
-                    irow = 1;
-                }
-                break;
-
-            case 4:
-                irow += 2;
-                if (irow >= (unsigned) m_frameContext->height()) {
-                    ipass++;
-                    irow = 0;
-                }
-                break;
-
-            default:
-                break;
-            }
-        } while (irow > (unsigned) (m_frameContext->height() - 1));
-    }
-}
-
-// Perform Lempel-Ziv-Welch decoding.
-// Returns true if decoding was successful. In this case the block will have been completely consumed and/or rowsRemaining will be 0.
-// Otherwise, decoding failed; returns false in this case, which will always cause the SkGifImageReader to set the "decode failed" flag.
-bool SkGIFLZWContext::doLZW(const unsigned char* block, size_t bytesInBlock)
-{
-    if (rowIter == rowBuffer.end())
-        return true;
-    const int width = m_frameContext->width();
-
-    for (const unsigned char* ch = block; bytesInBlock-- > 0; ch++) {
-        // Feed the next byte into the decoder's 32-bit input buffer.
-        datum += ((int) *ch) << bits;
-        bits += 8;
-
-        // Check for underflow of decoder's 32-bit input buffer.
-        while (bits >= codesize) {
-            // Get the leading variable-length symbol from the data stream.
-            int code = datum & codemask;
-            datum >>= codesize;
-            bits -= codesize;
-
-            // Reset the dictionary to its original state, if requested.
-            if (code == clearCode) {
-                codesize = m_frameContext->dataSize() + 1;
-                codemask = (1 << codesize) - 1;
-                avail = clearCode + 2;
-                oldcode = -1;
-                continue;
-            }
-
-            // Check for explicit end-of-stream code.
-            if (code == (clearCode + 1)) {
-                // end-of-stream should only appear after all image data.
-                if (!rowsRemaining)
-                    return true;
-                return false;
-            }
-
-            const int tempCode = code;
-            if (code > avail) {
-                // This is an invalid code. The dictionary is just initialized
-                // and the code is incomplete. We don't know how to handle
-                // this case.
-                return false;
-            }
-
-            if (code == avail) {
-                if (oldcode != -1) {
-                    // This is a new code just being added to the dictionary.
-                    // It must encode the contents of the previous code, plus
-                    // the first character of the previous code again.
-                    // Now we know avail is the new code we can use oldcode
-                    // value to get the code related to that.
-                    code = oldcode;
-                } else {
-                    // This is an invalid code. The dictionary is just initialized
-                    // and the code is incomplete. We don't know how to handle
-                    // this case.
-                    return false;
-                }
-            }
-
-            // code length of the oldcode for new code which is
-            // avail = oldcode + firstchar of the oldcode
-            int remaining = suffixLength[code];
-
-            // Round remaining up to multiple of SK_DICTIONARY_WORD_SIZE, because that's
-            // the granularity of the chunks we copy.  The last chunk may contain
-            // some garbage but it'll be overwritten by the next code or left unused.
-            // The working buffer is padded to account for this.
-            remaining += -remaining & (SK_DICTIONARY_WORD_SIZE - 1) ;
-            unsigned char* p = rowIter + remaining;
-
-            // Place rowIter so that after writing pixels rowIter can be set to firstchar, thereby
-            // completing the code.
-            rowIter += suffixLength[code];
-
-            while (remaining > 0) {
-                p -= SK_DICTIONARY_WORD_SIZE;
-                std::copy_n(suffix[code].begin(), SK_DICTIONARY_WORD_SIZE, p);
-                code = prefix[code];
-                remaining -= SK_DICTIONARY_WORD_SIZE;
-            }
-            const int firstchar = static_cast<unsigned char>(code);  // (strictly `suffix[code][0]`)
-
-            // This completes the new code avail and writing the corresponding
-            // pixels on target.
-            if (tempCode == avail) {
-                *rowIter++ = firstchar;
-            }
-
-            // Define a new codeword in the dictionary as long as we've read
-            // more than one value from the stream.
-            if (avail < SK_MAX_DICTIONARY_ENTRIES && oldcode != -1) {
-                // now add avail to the dictionary for future reference
-                unsigned short codeLength = suffixLength[oldcode] + 1;
-                int l = (codeLength - 1) & (SK_DICTIONARY_WORD_SIZE - 1);
-                // If the suffix buffer is full (l == 0) then oldcode becomes the new
-                // prefix, otherwise copy and extend oldcode's buffer and use the same
-                // prefix as oldcode used.
-                prefix[avail] = (l == 0) ? oldcode : prefix[oldcode];
-                suffix[avail] = suffix[oldcode];
-                suffix[avail][l] = firstchar;
-                suffixLength[avail] = codeLength;
-                ++avail;
-
-                // If we've used up all the codewords of a given length
-                // increase the length of codewords by one bit, but don't
-                // exceed the specified maximum codeword size.
-                if (!(avail & codemask) && avail < SK_MAX_DICTIONARY_ENTRIES) {
-                    ++codesize;
-                    codemask += avail;
-                }
-            }
-            oldcode = tempCode;
-
-            // Output as many rows as possible.
-            unsigned char* rowBegin = rowBuffer.begin();
-            for (; rowBegin + width <= rowIter; rowBegin += width) {
-                outputRow(rowBegin);
-                rowsRemaining--;
-                if (!rowsRemaining)
-                    return true;
-            }
-
-            if (rowBegin != rowBuffer.begin()) {
-                // Move the remaining bytes to the beginning of the buffer.
-                const size_t bytesToCopy = rowIter - rowBegin;
-                memcpy(&rowBuffer.front(), rowBegin, bytesToCopy);
-                rowIter = rowBuffer.begin() + bytesToCopy;
-            }
-        }
-    }
-    return true;
-}
-
-sk_sp<SkColorTable> SkGIFColorMap::buildTable(SkStreamBuffer* streamBuffer, SkColorType colorType,
-                                              int transparentPixel) const
-{
-    if (!m_isDefined)
-        return nullptr;
-
-    const PackColorProc proc = choose_pack_color_proc(false, colorType);
-    if (m_table && proc == m_packColorProc && m_transPixel == transparentPixel) {
-        SkASSERT(transparentPixel == kNotFound || transparentPixel > m_table->count()
-                || m_table->operator[](transparentPixel) == SK_ColorTRANSPARENT);
-        // This SkColorTable has already been built with the same transparent color and
-        // packing proc. Reuse it.
-        return m_table;
-    }
-    m_packColorProc = proc;
-    m_transPixel = transparentPixel;
-
-    const size_t bytes = m_colors * SK_BYTES_PER_COLORMAP_ENTRY;
-    sk_sp<SkData> rawData(streamBuffer->getDataAtPosition(m_position, bytes));
-    if (!rawData) {
-        return nullptr;
-    }
-
-    SkASSERT(m_colors <= SK_MAX_COLORS);
-    const uint8_t* srcColormap = rawData->bytes();
-    SkPMColor colorStorage[SK_MAX_COLORS];
-    for (int i = 0; i < m_colors; i++) {
-        if (i == transparentPixel) {
-            colorStorage[i] = SK_ColorTRANSPARENT;
-        } else {
-            colorStorage[i] = proc(255, srcColormap[0], srcColormap[1], srcColormap[2]);
-        }
-        srcColormap += SK_BYTES_PER_COLORMAP_ENTRY;
-    }
-    for (int i = m_colors; i < SK_MAX_COLORS; i++) {
-        colorStorage[i] = SK_ColorTRANSPARENT;
-    }
-    m_table = sk_sp<SkColorTable>(new SkColorTable(colorStorage, SK_MAX_COLORS));
-    return m_table;
-}
-
-sk_sp<SkColorTable> SkGifImageReader::getColorTable(SkColorType colorType, int index) {
-    if (index < 0 || index >= m_frames.count()) {
-        return nullptr;
-    }
-
-    const SkGIFFrameContext* frameContext = m_frames[index].get();
-    const SkGIFColorMap& localColorMap = frameContext->localColorMap();
-    const int transPix = frameContext->transparentPixel();
-    if (localColorMap.isDefined()) {
-        return localColorMap.buildTable(&m_streamBuffer, colorType, transPix);
-    }
-    if (m_globalColorMap.isDefined()) {
-        return m_globalColorMap.buildTable(&m_streamBuffer, colorType, transPix);
-    }
-    return nullptr;
-}
-
-// Perform decoding for this frame. frameComplete will be true if the entire frame is decoded.
-// Returns false if a decoding error occurred. This is a fatal error and causes the SkGifImageReader to set the "decode failed" flag.
-// Otherwise, either not enough data is available to decode further than before, or the new data has been decoded successfully; returns true in this case.
-bool SkGIFFrameContext::decode(SkStreamBuffer* streamBuffer, SkGifCodec* client,
-                               bool* frameComplete)
-{
-    *frameComplete = false;
-    if (!m_lzwContext) {
-        // Wait for more data to properly initialize SkGIFLZWContext.
-        if (!isDataSizeDefined() || !isHeaderDefined())
-            return true;
-
-        m_lzwContext.reset(new SkGIFLZWContext(client, this));
-        if (!m_lzwContext->prepareToDecode()) {
-            m_lzwContext.reset();
-            return false;
-        }
-
-        m_currentLzwBlock = 0;
-    }
-
-    // Some bad GIFs have extra blocks beyond the last row, which we don't want to decode.
-    while (m_currentLzwBlock < m_lzwBlocks.count() && m_lzwContext->hasRemainingRows()) {
-        const auto& block = m_lzwBlocks[m_currentLzwBlock];
-        const size_t len = block.blockSize;
-
-        sk_sp<SkData> data(streamBuffer->getDataAtPosition(block.blockPosition, len));
-        if (!data) {
-            return false;
-        }
-        if (!m_lzwContext->doLZW(reinterpret_cast<const unsigned char*>(data->data()), len)) {
-            return false;
-        }
-        ++m_currentLzwBlock;
-    }
-
-    // If this frame is data complete then the previous loop must have completely decoded all LZW blocks.
-    // There will be no more decoding for this frame so it's time to cleanup.
-    if (isComplete()) {
-        *frameComplete = true;
-        m_lzwContext.reset();
-    }
-    return true;
-}
-
-// Decode a frame.
-// This method uses SkGIFFrameContext:decode() to decode the frame; decoding error is reported to client as a critical failure.
-// Return true if decoding has progressed. Return false if an error has occurred.
-bool SkGifImageReader::decode(int frameIndex, bool* frameComplete)
-{
-    SkGIFFrameContext* currentFrame = m_frames[frameIndex].get();
-
-    return currentFrame->decode(&m_streamBuffer, m_client, frameComplete);
-}
-
-// Parse incoming GIF data stream into internal data structures.
-SkCodec::Result SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query)
-{
-    if (m_parseCompleted) {
-        return SkCodec::kSuccess;
-    }
-
-    if (SkGIFLoopCountQuery == query && m_loopCount != cLoopCountNotSeen) {
-        // Loop count has already been parsed.
-        return SkCodec::kSuccess;
-    }
-
-    // SkGIFSizeQuery and SkGIFFrameCountQuery are negative, so this is only meaningful when >= 0.
-    const int lastFrameToParse = (int) query;
-    if (lastFrameToParse >= 0 && m_frames.count() > lastFrameToParse
-                && m_frames[lastFrameToParse]->isComplete()) {
-        // We have already parsed this frame.
-        return SkCodec::kSuccess;
-    }
-
-    while (true) {
-        if (!m_streamBuffer.buffer(m_bytesToConsume)) {
-            // The stream does not yet have enough data.
-            return SkCodec::kIncompleteInput;
-        }
-
-        switch (m_state) {
-        case SkGIFLZW: {
-            SkASSERT(!m_frames.empty());
-            auto* frame = m_frames.back().get();
-            frame->addLzwBlock(m_streamBuffer.markPosition(), m_bytesToConsume);
-            GETN(1, SkGIFSubBlock);
-            break;
-        }
-        case SkGIFLZWStart: {
-            SkASSERT(!m_frames.empty());
-            auto* currentFrame = m_frames.back().get();
-
-            currentFrame->setDataSize(this->getOneByte());
-            GETN(1, SkGIFSubBlock);
-            break;
-        }
-
-        case SkGIFType: {
-            const char* currentComponent = m_streamBuffer.get();
-
-            // All GIF files begin with "GIF87a" or "GIF89a".
-            if (!memcmp(currentComponent, "GIF89a", 6))
-                m_version = 89;
-            else if (!memcmp(currentComponent, "GIF87a", 6))
-                m_version = 87;
-            else {
-                // This prevents attempting to continue reading this invalid stream.
-                GETN(0, SkGIFDone);
-                return SkCodec::kInvalidInput;
-            }
-            GETN(7, SkGIFGlobalHeader);
-            break;
-        }
-
-        case SkGIFGlobalHeader: {
-            const unsigned char* currentComponent =
-                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-            // This is the height and width of the "screen" or frame into which
-            // images are rendered. The individual images can be smaller than
-            // the screen size and located with an origin anywhere within the
-            // screen.
-            // Note that we don't inform the client of the size yet, as it might
-            // change after we read the first frame's image header.
-            fScreenWidth = GETINT16(currentComponent);
-            fScreenHeight = GETINT16(currentComponent + 2);
-
-            const int globalColorMapColors = 2 << (currentComponent[4] & 0x07);
-
-            if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* global map */
-                m_globalColorMap.setNumColors(globalColorMapColors);
-                GETN(SK_BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, SkGIFGlobalColormap);
-                break;
-            }
-
-            GETN(1, SkGIFImageStart);
-            break;
-        }
-
-        case SkGIFGlobalColormap: {
-            m_globalColorMap.setTablePosition(m_streamBuffer.markPosition());
-            GETN(1, SkGIFImageStart);
-            break;
-        }
-
-        case SkGIFImageStart: {
-            const char currentComponent = m_streamBuffer.get()[0];
-
-            if (currentComponent == '!') { // extension.
-                GETN(2, SkGIFExtension);
-                break;
-            }
-
-            if (currentComponent == ',') { // image separator.
-                GETN(9, SkGIFImageHeader);
-                break;
-            }
-
-            // If we get anything other than ',' (image separator), '!'
-            // (extension), or ';' (trailer), there is extraneous data
-            // between blocks. The GIF87a spec tells us to keep reading
-            // until we find an image separator, but GIF89a says such
-            // a file is corrupt. We follow Mozilla's implementation and
-            // proceed as if the file were correctly terminated, so the
-            // GIF will display.
-            GETN(0, SkGIFDone);
-            break;
-        }
-
-        case SkGIFExtension: {
-            const unsigned char* currentComponent =
-                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-            size_t bytesInBlock = currentComponent[1];
-            SkGIFState exceptionState = SkGIFSkipBlock;
-
-            switch (*currentComponent) {
-            case 0xf9:
-                // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes,
-                exceptionState = SkGIFControlExtension;
-                // and the parser for this block reads 4 bytes, so we must enforce that the buffer
-                // contains at least this many bytes. If the GIF specifies a different length, we
-                // allow that, so long as it's larger; the additional data will simply be ignored.
-                bytesInBlock = std::max(bytesInBlock, static_cast<size_t>(4));
-                break;
-
-            // The GIF spec also specifies the lengths of the following two extensions' headers
-            // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely
-            // and sanity-check the actual length of the application extension header before reading it,
-            // we allow GIFs to deviate from these values in either direction. This is important for
-            // real-world compatibility, as GIFs in the wild exist with application extension headers
-            // that are both shorter and longer than 11 bytes.
-            case 0x01:
-                // ignoring plain text extension
-                break;
-
-            case 0xff:
-                exceptionState = SkGIFApplicationExtension;
-                break;
-
-            case 0xfe:
-                exceptionState = SkGIFConsumeComment;
-                break;
-            }
-
-            if (bytesInBlock)
-                GETN(bytesInBlock, exceptionState);
-            else
-                GETN(1, SkGIFImageStart);
-            break;
-        }
-
-        case SkGIFConsumeBlock: {
-            const unsigned char currentComponent = this->getOneByte();
-            if (!currentComponent)
-                GETN(1, SkGIFImageStart);
-            else
-                GETN(currentComponent, SkGIFSkipBlock);
-            break;
-        }
-
-        case SkGIFSkipBlock: {
-            GETN(1, SkGIFConsumeBlock);
-            break;
-        }
-
-        case SkGIFControlExtension: {
-            const unsigned char* currentComponent =
-                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-            addFrameIfNecessary();
-            SkGIFFrameContext* currentFrame = m_frames.back().get();
-            if (*currentComponent & 0x1)
-                currentFrame->setTransparentPixel(currentComponent[3]);
-
-            // We ignore the "user input" bit.
-
-            // NOTE: This relies on the values in the FrameDisposalMethod enum
-            // matching those in the GIF spec!
-            int rawDisposalMethod = ((*currentComponent) >> 2) & 0x7;
-            switch (rawDisposalMethod) {
-            case 1:
-            case 2:
-            case 3:
-                currentFrame->setDisposalMethod((SkCodecAnimation::DisposalMethod) rawDisposalMethod);
-                break;
-            case 4:
-                // Some specs say that disposal method 3 is "overwrite previous", others that setting
-                // the third bit of the field (i.e. method 4) is. We map both to the same value.
-                currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kRestorePrevious);
-                break;
-            default:
-                // Other values use the default.
-                currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep);
-                break;
-            }
-            currentFrame->setDuration(GETINT16(currentComponent + 1) * 10);
-            GETN(1, SkGIFConsumeBlock);
-            break;
-        }
-
-        case SkGIFCommentExtension: {
-            const unsigned char currentComponent = this->getOneByte();
-            if (currentComponent)
-                GETN(currentComponent, SkGIFConsumeComment);
-            else
-                GETN(1, SkGIFImageStart);
-            break;
-        }
-
-        case SkGIFConsumeComment: {
-            GETN(1, SkGIFCommentExtension);
-            break;
-        }
-
-        case SkGIFApplicationExtension: {
-            // Check for netscape application extension.
-            if (m_bytesToConsume == 11) {
-                const unsigned char* currentComponent =
-                    reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-                if (!memcmp(currentComponent, "NETSCAPE2.0", 11) || !memcmp(currentComponent, "ANIMEXTS1.0", 11))
-                    GETN(1, SkGIFNetscapeExtensionBlock);
-            }
-
-            if (m_state != SkGIFNetscapeExtensionBlock)
-                GETN(1, SkGIFConsumeBlock);
-            break;
-        }
-
-        // Netscape-specific GIF extension: animation looping.
-        case SkGIFNetscapeExtensionBlock: {
-            const int currentComponent = this->getOneByte();
-            // SkGIFConsumeNetscapeExtension always reads 3 bytes from the stream; we should at least wait for this amount.
-            if (currentComponent)
-                GETN(std::max(3, currentComponent), SkGIFConsumeNetscapeExtension);
-            else
-                GETN(1, SkGIFImageStart);
-            break;
-        }
-
-        // Parse netscape-specific application extensions
-        case SkGIFConsumeNetscapeExtension: {
-            const unsigned char* currentComponent =
-                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-            int netscapeExtension = currentComponent[0] & 7;
-
-            // Loop entire animation specified # of times. Only read the loop count during the first iteration.
-            if (netscapeExtension == 1) {
-                m_loopCount = GETINT16(currentComponent + 1);
-
-                // Zero loop count is infinite animation loop request.
-                if (!m_loopCount)
-                    m_loopCount = SkCodec::kRepetitionCountInfinite;
-
-                GETN(1, SkGIFNetscapeExtensionBlock);
-
-                if (SkGIFLoopCountQuery == query) {
-                    m_streamBuffer.flush();
-                    return SkCodec::kSuccess;
-                }
-            } else if (netscapeExtension == 2) {
-                // Wait for specified # of bytes to enter buffer.
-
-                // Don't do this, this extension doesn't exist (isn't used at all)
-                // and doesn't do anything, as our streaming/buffering takes care of it all...
-                // See: http://semmix.pl/color/exgraf/eeg24.htm
-                GETN(1, SkGIFNetscapeExtensionBlock);
-            } else {
-                // 0,3-7 are yet to be defined netscape extension codes
-                // This prevents attempting to continue reading this invalid stream.
-                GETN(0, SkGIFDone);
-                return SkCodec::kInvalidInput;
-            }
-            break;
-        }
-
-        case SkGIFImageHeader: {
-            int height, width, xOffset, yOffset;
-            const unsigned char* currentComponent =
-                reinterpret_cast<const unsigned char*>(m_streamBuffer.get());
-
-            /* Get image offsets, with respect to the screen origin */
-            xOffset = GETINT16(currentComponent);
-            yOffset = GETINT16(currentComponent + 2);
-
-            /* Get image width and height. */
-            width  = GETINT16(currentComponent + 4);
-            height = GETINT16(currentComponent + 6);
-
-            // Some GIF files have frames that don't fit in the specified
-            // overall image size. For the first frame, we can simply enlarge
-            // the image size to allow the frame to be visible.  We can't do
-            // this on subsequent frames because the rest of the decoding
-            // infrastructure assumes the image size won't change as we
-            // continue decoding, so any subsequent frames that are even
-            // larger will be cropped.
-            // Luckily, handling just the first frame is sufficient to deal
-            // with most cases, e.g. ones where the image size is erroneously
-            // set to zero, since usually the first frame completely fills
-            // the image.
-            if (currentFrameIsFirstFrame()) {
-                fScreenHeight = std::max(fScreenHeight, yOffset + height);
-                fScreenWidth = std::max(fScreenWidth, xOffset + width);
-            }
-
-            // NOTE: Chromium placed this block after setHeaderDefined, down
-            // below we returned true when asked for the size. So Chromium
-            // created an image which would fail. Is this the correct behavior?
-            // We choose to return false early, so we will not create an
-            // SkCodec.
-
-            // Work around more broken GIF files that have zero image width or
-            // height.
-            if (!height || !width) {
-                height = fScreenHeight;
-                width = fScreenWidth;
-                if (!height || !width) {
-                    // This prevents attempting to continue reading this invalid stream.
-                    GETN(0, SkGIFDone);
-                    return SkCodec::kInvalidInput;
-                }
-            }
-
-            const bool isLocalColormapDefined = SkToBool(currentComponent[8] & 0x80);
-            // The three low-order bits of currentComponent[8] specify the bits per pixel.
-            const int numColors = 2 << (currentComponent[8] & 0x7);
-            if (currentFrameIsFirstFrame()) {
-                const int transPix = m_frames.empty() ? SkGIFColorMap::kNotFound
-                                                      : m_frames[0]->transparentPixel();
-                if (is_palette_index_valid(transPix)) {
-                    m_firstFrameHasAlpha = true;
-                } else {
-                    const bool frameIsSubset = xOffset > 0 || yOffset > 0
-                            || width < fScreenWidth
-                            || height < fScreenHeight;
-                    m_firstFrameHasAlpha = frameIsSubset;
-                }
-            }
-
-            addFrameIfNecessary();
-            SkGIFFrameContext* currentFrame = m_frames.back().get();
-            currentFrame->setHeaderDefined();
-
-            if (query == SkGIFSizeQuery) {
-                // The decoder needs to stop, so we return here, before
-                // flushing the buffer. Next time through, we'll be in the same
-                // state, requiring the same amount in the buffer.
-                return SkCodec::kSuccess;
-            }
-
-
-            currentFrame->setXYWH(xOffset, yOffset, width, height);
-            currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40));
-
-            // Overlaying interlaced, transparent GIFs over
-            // existing image data using the Haeberli display hack
-            // requires saving the underlying image in order to
-            // avoid jaggies at the transparency edges. We are
-            // unprepared to deal with that, so don't display such
-            // images progressively. Which means only the first
-            // frame can be progressively displayed.
-            // FIXME: It is possible that a non-transparent frame
-            // can be interlaced and progressively displayed.
-            currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame());
-
-            if (isLocalColormapDefined) {
-                currentFrame->localColorMap().setNumColors(numColors);
-                GETN(SK_BYTES_PER_COLORMAP_ENTRY * numColors, SkGIFImageColormap);
-                break;
-            }
-
-            setAlphaAndRequiredFrame(currentFrame);
-            GETN(1, SkGIFLZWStart);
-            break;
-        }
-
-        case SkGIFImageColormap: {
-            SkASSERT(!m_frames.empty());
-            auto* currentFrame = m_frames.back().get();
-            auto& cmap = currentFrame->localColorMap();
-            cmap.setTablePosition(m_streamBuffer.markPosition());
-            setAlphaAndRequiredFrame(currentFrame);
-            GETN(1, SkGIFLZWStart);
-            break;
-        }
-
-        case SkGIFSubBlock: {
-            const size_t bytesInBlock = this->getOneByte();
-            if (bytesInBlock)
-                GETN(bytesInBlock, SkGIFLZW);
-            else {
-                // Finished parsing one frame; Process next frame.
-                SkASSERT(!m_frames.empty());
-                // Note that some broken GIF files do not have enough LZW blocks to fully
-                // decode all rows but we treat it as frame complete.
-                m_frames.back()->setComplete();
-                GETN(1, SkGIFImageStart);
-                if (lastFrameToParse >= 0 && m_frames.count() > lastFrameToParse) {
-                    m_streamBuffer.flush();
-                    return SkCodec::kSuccess;
-                }
-            }
-            break;
-        }
-
-        case SkGIFDone: {
-            m_parseCompleted = true;
-            return SkCodec::kSuccess;
-        }
-
-        default:
-            // We shouldn't ever get here.
-            // This prevents attempting to continue reading this invalid stream.
-            GETN(0, SkGIFDone);
-            return SkCodec::kInvalidInput;
-            break;
-        }   // switch
-        m_streamBuffer.flush();
-    }
-}
-
-void SkGifImageReader::addFrameIfNecessary()
-{
-    if (m_frames.empty() || m_frames.back()->isComplete()) {
-        const int i = m_frames.count();
-        m_frames.emplace_back(new SkGIFFrameContext(i));
-    }
-}
-
-SkEncodedInfo::Alpha SkGIFFrameContext::onReportedAlpha() const {
-    // Note: We could correct these after decoding - i.e. some frames may turn out to be
-    // independent and opaque if they do not use the transparent pixel, but that would require
-    // checking whether each pixel used the transparent index.
-    return is_palette_index_valid(this->transparentPixel()) ? SkEncodedInfo::kBinary_Alpha
-                                                            : SkEncodedInfo::kOpaque_Alpha;
-}
-
-// FIXME: Move this method to close to doLZW().
-bool SkGIFLZWContext::prepareToDecode()
-{
-    SkASSERT(m_frameContext->isDataSizeDefined() && m_frameContext->isHeaderDefined());
-
-    // Since we use a codesize of 1 more than the datasize, we need to ensure
-    // that our datasize is strictly less than the SK_MAX_DICTIONARY_ENTRY_BITS.
-    if (m_frameContext->dataSize() >= SK_MAX_DICTIONARY_ENTRY_BITS)
-        return false;
-    clearCode = 1 << m_frameContext->dataSize();
-    avail = clearCode + 2;
-    oldcode = -1;
-    codesize = m_frameContext->dataSize() + 1;
-    codemask = (1 << codesize) - 1;
-    datum = bits = 0;
-    ipass = m_frameContext->interlaced() ? 1 : 0;
-    irow = 0;
-
-    // We want to know the longest sequence encodable by a dictionary with
-    // SK_MAX_DICTIONARY_ENTRIES entries. If we ignore the need to encode the base
-    // values themselves at the beginning of the dictionary, as well as the need
-    // for a clear code or a termination code, we could use every entry to
-    // encode a series of multiple values. If the input value stream looked
-    // like "AAAAA..." (a long string of just one value), the first dictionary
-    // entry would encode AA, the next AAA, the next AAAA, and so forth. Thus
-    // the longest sequence would be SK_MAX_DICTIONARY_ENTRIES + 1 values.
-    //
-    // However, we have to account for reserved entries. The first |datasize|
-    // bits are reserved for the base values, and the next two entries are
-    // reserved for the clear code and termination code. In theory a GIF can
-    // set the datasize to 0, meaning we have just two reserved entries, making
-    // the longest sequence (SK_MAX_DICTIONARY_ENTIRES + 1) - 2 values long. Since
-    // each value is a byte, this is also the number of bytes in the longest
-    // encodable sequence.
-    constexpr size_t kMaxSequence = SK_MAX_DICTIONARY_ENTRIES - 1;
-    constexpr size_t kMaxBytes = (kMaxSequence + SK_DICTIONARY_WORD_SIZE - 1)
-                         & ~(SK_DICTIONARY_WORD_SIZE - 1);
-
-    // Now allocate the output buffer. We decode directly into this buffer
-    // until we have at least one row worth of data, then call outputRow().
-    // This means worst case we may have (row width - 1) bytes in the buffer
-    // and then decode a sequence |kMaxBytes| long to append.
-    rowBuffer.reset(m_frameContext->width() - 1 + kMaxBytes);
-    rowIter = rowBuffer.begin();
-    rowsRemaining = m_frameContext->height();
-
-    // Clearing the whole suffix table lets us be more tolerant of bad data.
-    for (int i = 0; i < clearCode; ++i) {
-        std::fill_n(suffix[i].begin(), SK_DICTIONARY_WORD_SIZE, 0);
-        suffix[i][0] = i;
-        suffixLength[i] = 1;
-        prefix[i] = i;  // ensure that we have a place to find firstchar
-    }
-    return true;
-}
-
diff --git a/third_party/gif/SkGifImageReader.h b/third_party/gif/SkGifImageReader.h
deleted file mode 100644
index b2db3eb..0000000
--- a/third_party/gif/SkGifImageReader.h
+++ /dev/null
@@ -1,398 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef SkGifImageReader_h
-#define SkGifImageReader_h
-
-// Define ourselves as the clientPtr.  Mozilla just hacked their C++ callback class into this old C decoder,
-// so we will too.
-class SkGifCodec;
-
-#include "include/codec/SkCodec.h"
-#include "include/codec/SkCodecAnimation.h"
-#include "include/core/SkData.h"
-#include "include/core/SkImageInfo.h"
-#include "include/private/SkTArray.h"
-#include "src/codec/SkCodecPriv.h"
-#include "src/codec/SkColorTable.h"
-#include "src/codec/SkFrameHolder.h"
-#include "src/codec/SkStreamBuffer.h"
-
-#include <array>
-#include <memory>
-
-typedef SkTArray<unsigned char, true> SkGIFRow;
-
-
-#define SK_MAX_DICTIONARY_ENTRY_BITS 12
-#define SK_MAX_DICTIONARY_ENTRIES    4096 // 2^SK_MAX_DICTIONARY_ENTRY_BITS
-#define SK_MAX_COLORS                256
-#define SK_BYTES_PER_COLORMAP_ENTRY  3
-#define SK_DICTIONARY_WORD_SIZE      8
-
-// List of possible parsing states.
-enum SkGIFState {
-    SkGIFType,
-    SkGIFGlobalHeader,
-    SkGIFGlobalColormap,
-    SkGIFImageStart,
-    SkGIFImageHeader,
-    SkGIFImageColormap,
-    SkGIFImageBody,
-    SkGIFLZWStart,
-    SkGIFLZW,
-    SkGIFSubBlock,
-    SkGIFExtension,
-    SkGIFControlExtension,
-    SkGIFConsumeBlock,
-    SkGIFSkipBlock,
-    SkGIFDone,
-    SkGIFCommentExtension,
-    SkGIFApplicationExtension,
-    SkGIFNetscapeExtensionBlock,
-    SkGIFConsumeNetscapeExtension,
-    SkGIFConsumeComment
-};
-
-class SkGIFFrameContext;
-class SkGIFColorMap;
-
-// LZW decoder state machine.
-class SkGIFLZWContext final : public SkNoncopyable {
-public:
-    SkGIFLZWContext(SkGifCodec* client, const SkGIFFrameContext* frameContext)
-        : codesize(0)
-        , codemask(0)
-        , clearCode(0)
-        , avail(0)
-        , oldcode(0)
-        , bits(0)
-        , datum(0)
-        , ipass(0)
-        , irow(0)
-        , rowsRemaining(0)
-        , rowIter(nullptr)
-        , m_client(client)
-        , m_frameContext(frameContext)
-    { }
-
-    bool prepareToDecode();
-    void outputRow(const unsigned char* rowBegin);
-    bool doLZW(const unsigned char* block, size_t bytesInBlock);
-    bool hasRemainingRows() { return SkToBool(rowsRemaining); }
-
-private:
-    // LZW decoding states and output states.
-    int codesize;
-    int codemask;
-    int clearCode; // Codeword used to trigger dictionary reset.
-    int avail; // Index of next available slot in dictionary.
-    int oldcode;
-    int bits; // Number of unread bits in "datum".
-    int datum; // 32-bit input buffer.
-    int ipass; // Interlace pass; Ranges 1-4 if interlaced.
-    size_t irow; // Current output row, starting at zero.
-    size_t rowsRemaining; // Rows remaining to be output.
-
-    unsigned short prefix[SK_MAX_DICTIONARY_ENTRIES];
-    std::array<std::array<unsigned char, SK_DICTIONARY_WORD_SIZE>,
-                SK_MAX_DICTIONARY_ENTRIES> suffix;
-    unsigned short suffixLength[SK_MAX_DICTIONARY_ENTRIES];
-    SkGIFRow rowBuffer; // Single scanline temporary buffer.
-    unsigned char* rowIter;
-
-    SkGifCodec* const m_client;
-    const SkGIFFrameContext* m_frameContext;
-};
-
-struct SkGIFLZWBlock {
- public:
-  SkGIFLZWBlock(size_t position, size_t size)
-      : blockPosition(position), blockSize(size) {}
-
-  size_t blockPosition;
-  size_t blockSize;
-};
-
-class SkGIFColorMap final {
-public:
-    static constexpr int kNotFound = -1;
-
-    SkGIFColorMap()
-        : m_isDefined(false)
-        , m_position(0)
-        , m_colors(0)
-        , m_transPixel(kNotFound)
-        , m_packColorProc(nullptr)
-    {
-    }
-
-    void setNumColors(int colors) {
-        SkASSERT(!m_colors);
-        SkASSERT(!m_position);
-
-        m_colors = colors;
-    }
-
-    void setTablePosition(size_t position) {
-        SkASSERT(!m_isDefined);
-
-        m_position = position;
-        m_isDefined = true;
-    }
-
-    int numColors() const { return m_colors; }
-
-    bool isDefined() const { return m_isDefined; }
-
-    // Build RGBA table using the data stream.
-    sk_sp<SkColorTable> buildTable(SkStreamBuffer*, SkColorType dstColorType,
-                                   int transparentPixel) const;
-
-private:
-    bool m_isDefined;
-    size_t m_position;
-    int m_colors;
-    // Cached values. If these match on a new request, we can reuse m_table.
-    mutable int m_transPixel;
-    mutable PackColorProc m_packColorProc;
-    mutable sk_sp<SkColorTable> m_table;
-};
-
-class SkGifImageReader;
-
-// LocalFrame output state machine.
-class SkGIFFrameContext : public SkFrame {
-public:
-    SkGIFFrameContext(int id)
-        : INHERITED(id)
-        , m_transparentPixel(SkGIFColorMap::kNotFound)
-        , m_dataSize(0)
-        , m_progressiveDisplay(false)
-        , m_interlaced(false)
-        , m_currentLzwBlock(0)
-        , m_isComplete(false)
-        , m_isHeaderDefined(false)
-        , m_isDataSizeDefined(false)
-    {
-    }
-
-    ~SkGIFFrameContext() override
-    {
-    }
-
-    void addLzwBlock(size_t position, size_t size)
-    {
-        m_lzwBlocks.emplace_back(position, size);
-    }
-
-    bool decode(SkStreamBuffer*, SkGifCodec* client, bool* frameDecoded);
-
-    int transparentPixel() const { return m_transparentPixel; }
-    void setTransparentPixel(int pixel) { m_transparentPixel = pixel; }
-
-    bool isComplete() const { return m_isComplete; }
-    void setComplete() { m_isComplete = true; }
-    bool isHeaderDefined() const { return m_isHeaderDefined; }
-    void setHeaderDefined() { m_isHeaderDefined = true; }
-    bool isDataSizeDefined() const { return m_isDataSizeDefined; }
-    int dataSize() const { return m_dataSize; }
-    void setDataSize(int size)
-    {
-        m_dataSize = size;
-        m_isDataSizeDefined = true;
-    }
-    bool progressiveDisplay() const { return m_progressiveDisplay; }
-    void setProgressiveDisplay(bool progressiveDisplay) { m_progressiveDisplay = progressiveDisplay; }
-    bool interlaced() const { return m_interlaced; }
-    void setInterlaced(bool interlaced) { m_interlaced = interlaced; }
-
-    void clearDecodeState() { m_lzwContext.reset(); }
-    const SkGIFColorMap& localColorMap() const { return m_localColorMap; }
-    SkGIFColorMap& localColorMap() { return m_localColorMap; }
-
-protected:
-    SkEncodedInfo::Alpha onReportedAlpha() const override;
-
-private:
-    int m_transparentPixel; // Index of transparent pixel. Value is kNotFound if there is no transparent pixel.
-    int m_dataSize;
-
-    bool m_progressiveDisplay; // If true, do Haeberli interlace hack.
-    bool m_interlaced; // True, if scanlines arrive interlaced order.
-
-    std::unique_ptr<SkGIFLZWContext> m_lzwContext;
-    // LZW blocks for this frame.
-    SkTArray<SkGIFLZWBlock> m_lzwBlocks;
-
-    SkGIFColorMap m_localColorMap;
-
-    int m_currentLzwBlock;
-    bool m_isComplete;
-    bool m_isHeaderDefined;
-    bool m_isDataSizeDefined;
-
-    typedef SkFrame INHERITED;
-};
-
-class SkGifImageReader final : public SkFrameHolder {
-public:
-    // This takes ownership of stream.
-    SkGifImageReader(std::unique_ptr<SkStream> stream)
-        : m_client(nullptr)
-        , m_state(SkGIFType)
-        , m_bytesToConsume(6) // Number of bytes for GIF type, either "GIF87a" or "GIF89a".
-        , m_version(0)
-        , m_loopCount(cLoopCountNotSeen)
-        , m_streamBuffer(std::move(stream))
-        , m_parseCompleted(false)
-        , m_firstFrameHasAlpha(false)
-    {
-    }
-
-    ~SkGifImageReader() override
-    {
-    }
-
-    void setClient(SkGifCodec* client) { m_client = client; }
-
-    // Option to pass to parse(). All enums are negative, because a non-negative value is used to
-    // indicate that the Reader should parse up to and including the frame indicated.
-    enum SkGIFParseQuery {
-        // Parse enough to determine the size. Note that this parses the first frame's header,
-        // since we may decide to expand based on the frame's dimensions.
-        SkGIFSizeQuery        = -1,
-        // Parse to the end, so we know about all frames.
-        SkGIFFrameCountQuery  = -2,
-        // Parse until we see the loop count.
-        SkGIFLoopCountQuery   = -3,
-    };
-
-    // Parse incoming GIF data stream into internal data structures.
-    // Non-negative values are used to indicate to parse through that frame.
-    SkCodec::Result parse(SkGIFParseQuery);
-
-    // Decode the frame indicated by frameIndex.
-    // frameComplete will be set to true if the frame is completely decoded.
-    // The method returns false if there is an error.
-    bool decode(int frameIndex, bool* frameComplete);
-
-    int imagesCount() const
-    {
-        const int frames = m_frames.count();
-        if (!frames) {
-            return 0;
-        }
-
-        // This avoids counting an empty frame when the file is truncated (or
-        // simply not yet complete) after receiving SkGIFControlExtension (and
-        // possibly SkGIFImageHeader) but before reading the color table. This
-        // ensures that we do not count a frame before we know its required
-        // frame.
-        return m_frames.back()->reachedStartOfData() ? frames : frames - 1;
-    }
-    int loopCount() const {
-        if (cLoopCountNotSeen == m_loopCount) {
-            return 0;
-        }
-        return m_loopCount;
-    }
-
-    const SkGIFColorMap& globalColorMap() const
-    {
-        return m_globalColorMap;
-    }
-
-    const SkGIFFrameContext* frameContext(int index) const
-    {
-        return index >= 0 && index < m_frames.count()
-                ? m_frames[index].get() : nullptr;
-    }
-
-    void clearDecodeState() {
-        for (int index = 0; index < m_frames.count(); index++) {
-            m_frames[index]->clearDecodeState();
-        }
-    }
-
-    // Return the color table for frame index (which may be the global color table).
-    sk_sp<SkColorTable> getColorTable(SkColorType dstColorType, int index);
-
-    bool firstFrameHasAlpha() const { return m_firstFrameHasAlpha; }
-
-protected:
-    const SkFrame* onGetFrame(int i) const override {
-        return static_cast<const SkFrame*>(this->frameContext(i));
-    }
-
-private:
-    // Requires that one byte has been buffered into m_streamBuffer.
-    unsigned char getOneByte() const {
-        return reinterpret_cast<const unsigned char*>(m_streamBuffer.get())[0];
-    }
-
-    void addFrameIfNecessary();
-    bool currentFrameIsFirstFrame() const
-    {
-        return m_frames.empty() || (m_frames.count() == 1 && !m_frames[0]->isComplete());
-    }
-
-    // Unowned pointer
-    SkGifCodec* m_client;
-
-    // Parsing state machine.
-    SkGIFState m_state; // Current decoder master state.
-    size_t m_bytesToConsume; // Number of bytes to consume for next stage of parsing.
-
-    // Global (multi-image) state.
-    int m_version; // Either 89 for GIF89 or 87 for GIF87.
-    SkGIFColorMap m_globalColorMap;
-
-    static constexpr int cLoopCountNotSeen = -2;
-    int m_loopCount; // Netscape specific extension block to control the number of animation loops a GIF renders.
-
-    SkTArray<std::unique_ptr<SkGIFFrameContext>> m_frames;
-
-    SkStreamBuffer m_streamBuffer;
-    bool m_parseCompleted;
-
-    // This value can be computed before we create a SkGIFFrameContext, so we
-    // store it here instead of on m_frames[0].
-    bool m_firstFrameHasAlpha;
-};
-
-#endif
diff --git a/third_party/skcms/skcms.cc b/third_party/skcms/skcms.cc
index 6b4d87b..cc5738d 100644
--- a/third_party/skcms/skcms.cc
+++ b/third_party/skcms/skcms.cc
@@ -137,10 +137,10 @@
                                                        , TF_HLGish* hlg = nullptr) {
     if (tf.g < 0 && (int)tf.g == tf.g) {
         // TODO: sanity checks for PQ/HLG like we do for sRGBish.
-        switch (-(int)tf.g) {
-            case PQish:     if (pq ) { memcpy(pq , &tf.a, sizeof(*pq )); } return PQish;
-            case HLGish:    if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGish;
-            case HLGinvish: if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGinvish;
+        switch ((int)tf.g) {
+            case -PQish:     if (pq ) { memcpy(pq , &tf.a, sizeof(*pq )); } return PQish;
+            case -HLGish:    if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGish;
+            case -HLGinvish: if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGinvish;
         }
         return Bad;
     }
@@ -230,7 +230,7 @@
     return l + (h-l)*t;
 }
 
-static float max_roundtrip_error(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) {
+float skcms_MaxRoundtripError(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) {
     uint32_t N = curve->table_entries > 256 ? curve->table_entries : 256;
     const float dx = 1.0f / (N - 1);
     float err = 0;
@@ -243,7 +243,7 @@
 }
 
 bool skcms_AreApproximateInverses(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf) {
-    return max_roundtrip_error(curve, inv_tf) < (1/512.0f);
+    return skcms_MaxRoundtripError(curve, inv_tf) < (1/512.0f);
 }
 
 // Additional ICC signature values that are only used internally
@@ -266,6 +266,7 @@
     skcms_Signature_mAB  = 0x6D414220,
 
     skcms_Signature_CHAD = 0x63686164,
+    skcms_Signature_WTPT = 0x77747074,
 
     // Type signatures
     skcms_Signature_curv = 0x63757276,
@@ -391,6 +392,12 @@
     return true;
 }
 
+bool skcms_GetWTPT(const skcms_ICCProfile* profile, float xyz[3]) {
+    skcms_ICCTag tag;
+    return skcms_GetTagBySignature(profile, skcms_Signature_WTPT, &tag) &&
+           read_tag_xyz(&tag, &xyz[0], &xyz[1], &xyz[2]);
+}
+
 static bool read_to_XYZD50(const skcms_ICCTag* rXYZ, const skcms_ICCTag* gXYZ,
                            const skcms_ICCTag* bXYZ, skcms_Matrix3x3* toXYZ) {
     return read_tag_xyz(rXYZ, &toXYZ->vals[0][0], &toXYZ->vals[1][0], &toXYZ->vals[2][0]) &&
@@ -1649,11 +1656,10 @@
 static float rg_nonlinear(float x,
                           const skcms_Curve* curve,
                           const skcms_TransferFunction* tf,
-                          const float P[3],
                           float dfdP[3]) {
     const float y = eval_curve(curve, x);
 
-    const float g = P[0],  a = P[1],  b = P[2],
+    const float g = tf->g, a = tf->a, b = tf->b,
                 c = tf->c, d = tf->d, f = tf->f;
 
     const float Y = fmaxf_(a*y + b, 0.0f),
@@ -1676,11 +1682,12 @@
 }
 
 static bool gauss_newton_step(const skcms_Curve* curve,
-                              const skcms_TransferFunction* tf,
-                              float P[3],
+                                    skcms_TransferFunction* tf,
                               float x0, float dx, int N) {
     // We'll sample x from the range [x0,x1] (both inclusive) N times with even spacing.
     //
+    // Let P = [ tf->g, tf->a, tf->b ] (the three terms that we're adjusting).
+    //
     // We want to do P' = P + (Jf^T Jf)^-1 Jf^T r(P),
     //   where r(P) is the residual vector
     //   and Jf is the Jacobian matrix of f(), ∂r/∂P.
@@ -1722,7 +1729,7 @@
         float x = x0 + i*dx;
 
         float dfdP[3] = {0,0,0};
-        float resid = rg_nonlinear(x,curve,tf,P, dfdP);
+        float resid = rg_nonlinear(x,curve,tf, dfdP);
 
         for (int r = 0; r < 3; r++) {
             for (int c = 0; c < 3; c++) {
@@ -1749,58 +1756,85 @@
 
     // 4) multiply inverse lhs by rhs
     skcms_Vector3 dP = mv_mul(&lhs_inv, &rhs);
-    P[0] += dP.vals[0];
-    P[1] += dP.vals[1];
-    P[2] += dP.vals[2];
-    return isfinitef_(P[0]) && isfinitef_(P[1]) && isfinitef_(P[2]);
+    tf->g += dP.vals[0];
+    tf->a += dP.vals[1];
+    tf->b += dP.vals[2];
+    return isfinitef_(tf->g) && isfinitef_(tf->a) && isfinitef_(tf->b);
 }
 
+static float max_roundtrip_error_checked(const skcms_Curve* curve,
+                                         const skcms_TransferFunction* tf_inv) {
+    skcms_TransferFunction tf;
+    if (!skcms_TransferFunction_invert(tf_inv, &tf) || sRGBish != classify(tf)) {
+        return INFINITY_;
+    }
+
+    skcms_TransferFunction tf_inv_again;
+    if (!skcms_TransferFunction_invert(&tf, &tf_inv_again)) {
+        return INFINITY_;
+    }
+
+    return skcms_MaxRoundtripError(curve, &tf_inv_again);
+}
 
 // Fit the points in [L,N) to the non-linear piece of tf, or return false if we can't.
 static bool fit_nonlinear(const skcms_Curve* curve, int L, int N, skcms_TransferFunction* tf) {
-    float P[3] = { tf->g, tf->a, tf->b };
+    // This enforces a few constraints that are not modeled in gauss_newton_step()'s optimization.
+    auto fixup_tf = [tf]() {
+        // a must be non-negative. That ensures the function is monotonically increasing.
+        // We don't really know how to fix up a if it goes negative.
+        if (tf->a < 0) {
+            return false;
+        }
+        // ad+b must be non-negative. That ensures we don't end up with complex numbers in powf.
+        // We feel just barely not uneasy enough to tweak b so ad+b is zero in this case.
+        if (tf->a * tf->d + tf->b < 0) {
+            tf->b = -tf->a * tf->d;
+        }
+        assert (tf->a >= 0 &&
+                tf->a * tf->d + tf->b >= 0);
+
+        // cd+f must be ~= (ad+b)^g+e. That ensures the function is continuous. We keep e as a free
+        // parameter so we can guarantee this.
+        tf->e =   tf->c*tf->d + tf->f
+          - powf_(tf->a*tf->d + tf->b, tf->g);
+
+        return true;
+    };
+
+    if (!fixup_tf()) {
+        return false;
+    }
 
     // No matter where we start, dx should always represent N even steps from 0 to 1.
     const float dx = 1.0f / (N-1);
 
+    skcms_TransferFunction best_tf = *tf;
+    float best_max_error = INFINITY_;
+
+    // Need this or several curves get worse... *sigh*
+    float init_error = max_roundtrip_error_checked(curve, tf);
+    if (init_error < best_max_error) {
+        best_max_error = init_error;
+        best_tf = *tf;
+    }
+
     // As far as we can tell, 1 Gauss-Newton step won't converge, and 3 steps is no better than 2.
-    for (int j = 0; j < 2; j++) {
-        // These extra constraints a >= 0 and ad+b >= 0 are not modeled in the optimization.
-        // We don't really know how to fix up a if it goes negative.
-        if (P[1] < 0) {
-            return false;
+    for (int j = 0; j < 8; j++) {
+        if (!gauss_newton_step(curve, tf, L*dx, dx, N-L) || !fixup_tf()) {
+            *tf = best_tf;
+            return isfinitef_(best_max_error);
         }
-        // If ad+b goes negative, we feel just barely not uneasy enough to tweak b so ad+b is zero.
-        if (P[1] * tf->d + P[2] < 0) {
-            P[2] = -P[1] * tf->d;
-        }
-        assert (P[1] >= 0 &&
-                P[1] * tf->d + P[2] >= 0);
 
-        if (!gauss_newton_step(curve, tf,
-                               P,
-                               L*dx, dx, N-L)) {
-            return false;
+        float max_error = max_roundtrip_error_checked(curve, tf);
+        if (max_error < best_max_error) {
+            best_max_error = max_error;
+            best_tf = *tf;
         }
     }
 
-    // We need to apply our fixups one last time
-    if (P[1] < 0) {
-        return false;
-    }
-    if (P[1] * tf->d + P[2] < 0) {
-        P[2] = -P[1] * tf->d;
-    }
-
-    assert (P[1] >= 0 &&
-            P[1] * tf->d + P[2] >= 0);
-
-    tf->g = P[0];
-    tf->a = P[1];
-    tf->b = P[2];
-    tf->e =   tf->c*tf->d + tf->f
-      - powf_(tf->a*tf->d + tf->b, tf->g);
-    return true;
+    *tf = best_tf;
+    return isfinitef_(best_max_error);
 }
 
 bool skcms_ApproximateCurve(const skcms_Curve* curve,
@@ -1867,7 +1901,9 @@
             }
 
             // We fit tf_inv, so calculate tf to keep in sync.
+            // fit_nonlinear() should guarantee invertibility.
             if (!skcms_TransferFunction_invert(&tf_inv, &tf)) {
+                assert(false);
                 continue;
             }
         }
@@ -1875,7 +1911,9 @@
         // We'd better have a sane, sRGB-ish TF by now.
         // Other non-Bad TFs would be fine, but we know we've only ever tried to fit sRGBish;
         // anything else is just some accident of math and the way we pun tf.g as a type flag.
+        // fit_nonlinear() should guarantee this.
         if (sRGBish != classify(tf)) {
+            assert(false);
             continue;
         }
 
@@ -1886,11 +1924,13 @@
         //
         // We've kept tf and tf_inv in sync above, but we can't guarantee that tf is
         // invertible, so re-verify that here (and use the new inverse for testing).
+        // fit_nonlinear() should guarantee this.
         if (!skcms_TransferFunction_invert(&tf, &tf_inv)) {
+            assert(false);
             continue;
         }
 
-        float err = max_roundtrip_error(curve, &tf_inv);
+        float err = skcms_MaxRoundtripError(curve, &tf_inv);
         if (*max_error > err) {
             *max_error = err;
             *approx    = tf;
@@ -2552,7 +2592,7 @@
 
         float err = 0;
         for (int j = 0; j < 3; ++j) {
-            err = fmaxf_(err, max_roundtrip_error(&profile->trc[j], &inv));
+            err = fmaxf_(err, skcms_MaxRoundtripError(&profile->trc[j], &inv));
         }
         if (min_max_error > err) {
             min_max_error = err;
diff --git a/third_party/skcms/skcms_internal.h b/third_party/skcms/skcms_internal.h
index 551128a..cc6d578b 100644
--- a/third_party/skcms/skcms_internal.h
+++ b/third_party/skcms/skcms_internal.h
@@ -21,8 +21,17 @@
 // ~~~~ General Helper Macros ~~~~
     #define ARRAY_COUNT(arr) (int)(sizeof((arr)) / sizeof(*(arr)))
 
-// ~~~~ skcms_ICCProfile ~~~~
-    bool skcms_GetCHAD(const skcms_ICCProfile* profile, skcms_Matrix3x3* m);
+    typedef struct skcms_ICCTag {
+        uint32_t       signature;
+        uint32_t       type;
+        uint32_t       size;
+        const uint8_t* buf;
+    } skcms_ICCTag;
+
+    void skcms_GetTagByIndex    (const skcms_ICCProfile*, uint32_t idx, skcms_ICCTag*);
+    bool skcms_GetTagBySignature(const skcms_ICCProfile*, uint32_t sig, skcms_ICCTag*);
+
+    float skcms_MaxRoundtripError(const skcms_Curve* curve, const skcms_TransferFunction* inv_tf);
 
     // 252 of a random shuffle of all possible bytes.
     // 252 is evenly divisible by 3 and 4.  Only 192, 10, 241, and 43 are missing.
diff --git a/third_party/skcms/version.sha1 b/third_party/skcms/version.sha1
index f6db96d..da36997 100755
--- a/third_party/skcms/version.sha1
+++ b/third_party/skcms/version.sha1
@@ -1 +1 @@
-8e28e18b5c9e38265362171570ccfedcbf662761
\ No newline at end of file
+ef3043bd8110d05330024238629faeace0acd232
\ No newline at end of file
diff --git a/tools/CrashHandler.cpp b/tools/CrashHandler.cpp
index 5a2186f..c769ebc 100644
--- a/tools/CrashHandler.cpp
+++ b/tools/CrashHandler.cpp
@@ -54,10 +54,48 @@
         // both 32 and 64 bit on bots.  Doesn't matter much: catchsegv is best anyway.
         #include <cxxabi.h>
         #include <dlfcn.h>
-        #include <execinfo.h>
         #include <string.h>
+#if defined(__Fuchsia__)
+        #include <stdint.h>
+
+        // syslog crash reporting from Fuchsia's backtrace_request.h
+        //
+        // Special value we put in the first register to let the exception handler know
+        // that we are just requesting a backtrace and we should resume the thread.
+        #define BACKTRACE_REQUEST_MAGIC ((uint64_t)0xee726573756d65ee)
+
+        // Prints a backtrace, resuming the thread without killing the process.
+        __attribute__((always_inline)) static inline void backtrace_request(void) {
+          // Two instructions: one that sets a software breakpoint ("int3" on x64,
+          // "brk" on arm64) and one that writes the "magic" value in the first
+          // register ("a" on x64, "x0" on arm64).
+          //
+          // We set a software breakpoint to trigger the exception handling in
+          // crashsvc, which will print the debug info, including the backtrace.
+          //
+          // We write the "magic" value in the first register so that the exception
+          // handler can check for it and resume the thread if present.
+          #ifdef __x86_64__
+            __asm__("int3" : : "a"(BACKTRACE_REQUEST_MAGIC));
+          #endif
+          #ifdef __aarch64__
+            // This is what gdb uses.
+            __asm__(
+                "mov x0, %0\n"
+                "\tbrk 0"
+                :
+                : "r"(BACKTRACE_REQUEST_MAGIC)
+                : "x0");
+          #endif
+        }
+#else
+        #include <execinfo.h>
+#endif
 
         static void handler(int sig) {
+#if defined(__Fuchsia__)
+            backtrace_request();
+#else
             void* stack[64];
             const int count = backtrace(stack, SK_ARRAY_COUNT(stack));
             char** symbols = backtrace_symbols(stack, count);
@@ -78,11 +116,11 @@
                 }
                 SkDebugf("    %s\n", symbols[i]);
             }
-
-            // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
+#endif
+            // Exit NOW.  Don't notify other threads, don't call anything registered with
+            // atexit().
             _Exit(sig);
         }
-
     #endif
 
     #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
diff --git a/tools/ProcStats.cpp b/tools/ProcStats.cpp
index b9eae31..5c7bb46 100644
--- a/tools/ProcStats.cpp
+++ b/tools/ProcStats.cpp
@@ -8,7 +8,23 @@
 #include "include/core/SkTypes.h"
 #include "tools/ProcStats.h"
 
-#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_ANDROID)
+#if defined(__Fuchsia__)
+    #include <zircon/process.h>
+    #include <zircon/syscalls.h>
+    #include <zircon/syscalls/object.h>
+    #include <zircon/types.h>
+
+    int sk_tools::getMaxResidentSetSizeMB() {
+      zx_info_task_stats_t task_stats;
+      zx_handle_t process = zx_process_self();
+      zx_status_t status = zx_object_get_info(
+      process, ZX_INFO_TASK_STATS, &task_stats, sizeof(task_stats), NULL, NULL);
+      if (status != ZX_OK) {
+        return -1;
+      }
+      return (task_stats.mem_private_bytes + task_stats.mem_shared_bytes) / (1 << 20);
+    }
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) || defined(SK_BUILD_FOR_ANDROID)
     #include <sys/resource.h>
     int sk_tools::getMaxResidentSetSizeMB() {
         struct rusage ru;
diff --git a/tools/SkVMBuilders.cpp b/tools/SkVMBuilders.cpp
index 1902b0e..d068482 100644
--- a/tools/SkVMBuilders.cpp
+++ b/tools/SkVMBuilders.cpp
@@ -58,9 +58,7 @@
     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));
+        return round(mul(f32, splat(255.0f)));
     };
     switch (dstFmt) {
         case Fmt::A8: {
diff --git a/tools/ToolUtils.cpp b/tools/ToolUtils.cpp
index 99e25ee..90abac4 100644
--- a/tools/ToolUtils.cpp
+++ b/tools/ToolUtils.cpp
@@ -229,7 +229,7 @@
 SkPath make_star(const SkRect& bounds, int numPts, int step) {
     SkASSERT(numPts != step);
     SkPath path;
-    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.setFillType(SkPathFillType::kEvenOdd);
     path.moveTo(0, -1);
     for (int i = 1; i < numPts; ++i) {
         int      idx   = i * step % numPts;
diff --git a/tools/android/measure_fps.py b/tools/android/measure_fps.py
old mode 100644
new mode 100755
diff --git a/tools/android/upload_to_android.py b/tools/android/upload_to_android.py
old mode 100644
new mode 100755
diff --git a/tools/calmbench/ab.py b/tools/calmbench/ab.py
old mode 100644
new mode 100755
diff --git a/tools/calmbench/calmbench.py b/tools/calmbench/calmbench.py
old mode 100644
new mode 100755
index 4bef485..9aa271a
--- a/tools/calmbench/calmbench.py
+++ b/tools/calmbench/calmbench.py
@@ -1,4 +1,4 @@
-#!/usr/bin/pyton
+#!/usr/bin/python
 
 # Copyright 2017 Google Inc.
 #
diff --git a/tools/chrome_release_branch.py b/tools/chrome_release_branch.py
old mode 100644
new mode 100755
diff --git a/tools/debugger/DrawCommand.cpp b/tools/debugger/DrawCommand.cpp
index 7571a48..44c4288 100644
--- a/tools/debugger/DrawCommand.cpp
+++ b/tools/debugger/DrawCommand.cpp
@@ -518,18 +518,18 @@
 
 void DrawCommand::MakeJsonPath(SkJSONWriter& writer, const SkPath& path) {
     writer.beginObject();
-    switch (path.getFillType()) {
-        case SkPath::kWinding_FillType:
+    switch (path.getNewFillType()) {
+        case SkPathFillType::kWinding:
             writer.appendString(DEBUGCANVAS_ATTRIBUTE_FILLTYPE, DEBUGCANVAS_FILLTYPE_WINDING);
             break;
-        case SkPath::kEvenOdd_FillType:
+        case SkPathFillType::kEvenOdd:
             writer.appendString(DEBUGCANVAS_ATTRIBUTE_FILLTYPE, DEBUGCANVAS_FILLTYPE_EVENODD);
             break;
-        case SkPath::kInverseWinding_FillType:
+        case SkPathFillType::kInverseWinding:
             writer.appendString(DEBUGCANVAS_ATTRIBUTE_FILLTYPE,
                                 DEBUGCANVAS_FILLTYPE_INVERSEWINDING);
             break;
-        case SkPath::kInverseEvenOdd_FillType:
+        case SkPathFillType::kInverseEvenOdd:
             writer.appendString(DEBUGCANVAS_ATTRIBUTE_FILLTYPE,
                                 DEBUGCANVAS_FILLTYPE_INVERSEEVENODD);
             break;
diff --git a/tools/embed_resources.py b/tools/embed_resources.py
old mode 100644
new mode 100755
diff --git a/tools/find_run_binary.py b/tools/find_run_binary.py
old mode 100644
new mode 100755
diff --git a/tools/fix_pythonpath.py b/tools/fix_pythonpath.py
old mode 100644
new mode 100755
diff --git a/tools/fm/fm.cpp b/tools/fm/fm.cpp
index d89bcac..e56d1e7 100644
--- a/tools/fm/fm.cpp
+++ b/tools/fm/fm.cpp
@@ -17,6 +17,7 @@
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpu.h"
 #include "src/utils/SkOSPath.h"
+#include "tests/Test.h"
 #include "tools/AutoreleasePool.h"
 #include "tools/CrashHandler.h"
 #include "tools/HashAndEncode.h"
@@ -33,11 +34,14 @@
 
 #if defined(SK_ENABLE_SKOTTIE)
     #include "modules/skottie/include/Skottie.h"
-    #include "modules/skottie/utils/SkottieUtils.h"
+    #include "modules/skresources/include/SkResources.h"
 #endif
 
 using sk_gpu_test::GrContextFactory;
 
+static DEFINE_bool(listGMs  , false, "Print GM names and exit.");
+static DEFINE_bool(listTests, false, "Print unit test names and exit.");
+
 static DEFINE_string2(sources, s, "", "Which GMs, .skps, or images to draw.");
 static DEFINE_string2(backend, b, "", "Backend used to create a canvas to draw into.");
 
@@ -66,6 +70,7 @@
 
 static DEFINE_bool   (cpuDetect, true, "Detect CPU features for runtime optimizations?");
 static DEFINE_string2(writePath, w, "", "Write .pngs to this directory if set.");
+static DEFINE_bool   (quick, false, "Skip image hashing and encoding?");
 
 static DEFINE_string(writeShaders, "", "Write GLSL shaders to this directory if set.");
 
@@ -200,6 +205,29 @@
 }
 #endif
 
+static void init(Source* source, const skiatest::Test& test) {
+    source->size  = {1,1};
+    source->draw  = [test](SkCanvas* canvas) {
+        struct Reporter : public skiatest::Reporter {
+            SkString msg;
+
+            void reportFailed(const skiatest::Failure& failure) override {
+                msg = failure.toString();
+            }
+        } reporter;
+
+        test.run(&reporter, GrContextOptions{});
+
+        if (reporter.msg.isEmpty()) {
+            canvas->clear(SK_ColorGREEN);
+            return ok;
+        }
+
+        canvas->clear(SK_ColorRED);
+        return fail(reporter.msg.c_str());
+    };
+}
+
 static sk_sp<SkImage> draw_with_cpu(std::function<bool(SkCanvas*)> draw,
                                     SkImageInfo info) {
     if (sk_sp<SkSurface> surface = SkSurface::MakeRaster(info)) {
@@ -363,15 +391,32 @@
     SkTHashMap<SkString, skiagm::GMFactory> gm_factories;
     for (skiagm::GMFactory factory : skiagm::GMRegistry::Range()) {
         std::unique_ptr<skiagm::GM> gm{factory()};
-        if (FLAGS_sources.isEmpty()) {
+        if (FLAGS_listGMs) {
             fprintf(stdout, "%s\n", gm->getName());
         } else {
             gm_factories.set(SkString{gm->getName()}, factory);
         }
     }
-    if (FLAGS_sources.isEmpty()) {
+
+    SkTHashMap<SkString, const skiatest::Test*> tests;
+    for (const skiatest::Test& test : skiatest::TestRegistry::Range()) {
+        if (test.needsGpu) {
+            continue;  // TODO
+        }
+        if (FLAGS_listTests) {
+            fprintf(stdout, "%s\n", test.name);
+        } else {
+            tests.set(SkString{test.name}, &test);
+        }
+    }
+
+    if (FLAGS_listGMs || FLAGS_listTests) {
         return 0;
     }
+    if (FLAGS_sources.isEmpty()) {
+        fprintf(stderr, "Please give me something to run using -s/--sources!\n");
+        return 1;
+    }
 
     SkTArray<Source> sources;
     for (const SkString& name : FLAGS_sources) {
@@ -384,6 +429,12 @@
             continue;
         }
 
+        if (const skiatest::Test** test = tests.find(name)) {
+            source->name = name;
+            init(source, **test);
+            continue;
+        }
+
         if (sk_sp<SkData> blob = SkData::MakeFromFileName(name.c_str())) {
             source->name = SkOSPath::Basename(name.c_str());
 
@@ -403,7 +454,7 @@
             else if (name.endsWith(".json")) {
                 const SkString dir  = SkOSPath::Dirname(name.c_str());
                 if (sk_sp<skottie::Animation> animation = skottie::Animation::Builder()
-                        .setResourceProvider(skottie_utils::FileResourceProvider::Make(dir))
+                        .setResourceProvider(skresources::FileResourceProvider::Make(dir))
                         .make((const char*)blob->data(), blob->size())) {
                     init(source, animation);
                     continue;
@@ -541,39 +592,44 @@
             continue;
         }
 
+        // We read back a bitmap even when --quick is set and we won't use it,
+        // to keep us honest about deferred work, flushing pipelines, etc.
         SkBitmap bitmap;
         if (image && !image->asLegacyBitmap(&bitmap)) {
             SK_ABORT("SkImage::asLegacyBitmap() failed.");
         }
 
-        HashAndEncode hashAndEncode{bitmap};
         SkString md5;
-        {
-            SkMD5 hash;
-            if (image) {
-                hashAndEncode.write(&hash);
-            } else {
-                hash.write(blob->data(), blob->size());
-            }
-
-            SkMD5::Digest digest = hash.finish();
-            for (int i = 0; i < 16; i++) {
-                md5.appendf("%02x", digest.data[i]);
-            }
-        }
-
-        if (!FLAGS_writePath.isEmpty()) {
-            sk_mkdir(FLAGS_writePath[0]);
-            SkString path = SkStringPrintf("%s/%s%s", FLAGS_writePath[0], source.name.c_str(), ext);
-
-            if (image) {
-                if (!hashAndEncode.writePngTo(path.c_str(), md5.c_str(),
-                                              FLAGS_key, FLAGS_properties)) {
-                    SK_ABORT("Could not write .png.");
+        if (!FLAGS_quick) {
+            HashAndEncode hashAndEncode{bitmap};
+            {
+                SkMD5 hash;
+                if (image) {
+                    hashAndEncode.write(&hash);
+                } else {
+                    hash.write(blob->data(), blob->size());
                 }
-            } else {
-                SkFILEWStream file(path.c_str());
-                file.write(blob->data(), blob->size());
+
+                SkMD5::Digest digest = hash.finish();
+                for (int i = 0; i < 16; i++) {
+                    md5.appendf("%02x", digest.data[i]);
+                }
+            }
+
+            if (!FLAGS_writePath.isEmpty()) {
+                sk_mkdir(FLAGS_writePath[0]);
+                SkString path = SkStringPrintf("%s/%s%s",
+                                               FLAGS_writePath[0], source.name.c_str(), ext);
+
+                if (image) {
+                    if (!hashAndEncode.writePngTo(path.c_str(), md5.c_str(),
+                                                  FLAGS_key, FLAGS_properties)) {
+                        SK_ABORT("Could not write .png.");
+                    }
+                } else {
+                    SkFILEWStream file(path.c_str());
+                    file.write(blob->data(), blob->size());
+                }
             }
         }
 
diff --git a/tools/fm/fm_bot/fm_bot.go b/tools/fm/fm_bot/fm_bot.go
index ef0efc2..2a3c532 100644
--- a/tools/fm/fm_bot/fm_bot.go
+++ b/tools/fm/fm_bot/fm_bot.go
@@ -47,17 +47,17 @@
 	flag.IntVar(gpuLimit, "g", *gpuLimit, "Alias for --gpuLimit.")
 }
 
-func listAllGMs(fm string) (gms []string, err error) {
-	// Query fm binary for list of all available GMs by running with no arguments.
-	cmd := exec.Command(fm)
+// Query fm binary for list of all available GMs/tests by running with --listGMs/--listTests.
+func listAll(flag string, fm string) (list []string, err error) {
+	cmd := exec.Command(fm, flag)
 	stdout, err := cmd.Output()
 	if err != nil {
 		return
 	}
-	// GM names are listed line-by-line.
+	// Names are listed line-by-line.
 	scanner := bufio.NewScanner(bytes.NewReader(stdout))
 	for scanner.Scan() {
-		gms = append(gms, scanner.Text())
+		list = append(list, scanner.Text())
 	}
 	err = scanner.Err()
 	return
@@ -68,7 +68,7 @@
 	Flags   []string
 }
 
-func parseWork(args []string, gms []string) (*work, error) {
+func parseWork(args []string, gms []string, tests []string) (*work, error) {
 	w := &work{}
 	for _, arg := range args {
 		// I wish we could parse flags here too, but it's too late.
@@ -90,6 +90,11 @@
 			w.Sources = append(w.Sources, gms...)
 			continue
 		}
+		// Same for tests.
+		if arg == "test" || arg == "tests" {
+			w.Sources = append(w.Sources, tests...)
+			continue
+		}
 
 		// Is this an option to pass through to fm?
 		if parts := strings.Split(arg, "="); len(parts) == 2 {
@@ -103,15 +108,21 @@
 			continue
 		}
 
-		// Is this argument naming a GM?
-		matchedAnyGM := false
+		// Is this argument naming a GM or test?
+		matched := false
 		for _, gm := range gms {
 			if (*exact && gm == arg) || (!*exact && strings.Contains(gm, arg)) {
 				w.Sources = append(w.Sources, gm)
-				matchedAnyGM = true
+				matched = true
 			}
 		}
-		if matchedAnyGM {
+		for _, test := range tests {
+			if (*exact && test == arg) || (!*exact && strings.Contains(test, arg)) {
+				w.Sources = append(w.Sources, test)
+				matched = true
+			}
+		}
+		if matched {
 			continue
 		}
 
@@ -148,11 +159,16 @@
 	}
 	fm := flag.Args()[0]
 
-	gms, err := listAllGMs(fm)
+	gms, err := listAll("--listGMs", fm)
 	if err != nil {
 		log.Fatalln("Could not query", fm, "for GMs:", err)
 	}
 
+	tests, err := listAll("--listTests", fm)
+	if err != nil {
+		log.Fatalln("Could not query", fm, "for tests:", err)
+	}
+
 	// One job can comes right on the command line,
 	// and any number can come one per line from -script.
 	jobs := [][]string{flag.Args()[1:]}
@@ -230,7 +246,7 @@
 			continue
 		}
 
-		w, err := parseWork(job, gms)
+		w, err := parseWork(job, gms, tests)
 		if err != nil {
 			log.Fatal(err)
 		}
diff --git a/tools/fonts/generate_fir_coeff.py b/tools/fonts/generate_fir_coeff.py
old mode 100644
new mode 100755
diff --git a/tools/git-sync-deps b/tools/git-sync-deps
index c7379c0..ff121b1 100755
--- a/tools/git-sync-deps
+++ b/tools/git-sync-deps
@@ -99,16 +99,17 @@
     return False
 
 
-def status(directory, checkoutable):
+def status(directory, commithash, change):
   def truncate(s, length):
     return s if len(s) <= length else s[:(length - 3)] + '...'
   dlen = 36
   directory = truncate(directory, dlen)
-  checkoutable = truncate(checkoutable, 40)
-  sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable))
+  commithash = truncate(commithash, 40)
+  symbol = '>' if change else '@'
+  sys.stdout.write('%-*s %s %s\n' % (dlen, directory, symbol, commithash))
 
 
-def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
+def git_checkout_to_directory(git, repo, commithash, directory, verbose):
   """Checkout (and clone if needed) a Git repository.
 
   Args:
@@ -117,8 +118,7 @@
     repo (string) the location of the repository, suitable
          for passing to `git clone`.
 
-    checkoutable (string) a tag, branch, or commit, suitable for
-                 passing to `git checkout`
+    commithash (string) a commit, suitable for passing to `git checkout`
 
     directory (string) the path into which the repository
               should be checked out.
@@ -129,7 +129,12 @@
   """
   if not os.path.isdir(directory):
     subprocess.check_call(
-      [git, 'clone', '--quiet', repo, directory])
+      [git, 'clone', '--quiet', '--no-checkout', repo, directory])
+    subprocess.check_call([git, 'checkout', '--quiet', commithash],
+                          cwd=directory)
+    if verbose:
+      status(directory, commithash, True)
+    return
 
   if not is_git_toplevel(git, directory):
     # if the directory exists, but isn't a git repo, you will modify
@@ -145,11 +150,11 @@
   with open(os.devnull, 'w') as devnull:
     # If this fails, we will fetch before trying again.  Don't spam user
     # with error infomation.
-    if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable],
+    if 0 == subprocess.call([git, 'checkout', '--quiet', commithash],
                             cwd=directory, stderr=devnull):
       # if this succeeds, skip slow `git fetch`.
       if verbose:
-        status(directory, checkoutable)  # Success.
+        status(directory, commithash, False)  # Success.
       return
 
   # If the repo has changed, always force use of the correct repo.
@@ -159,10 +164,10 @@
 
   subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory)
 
-  subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory)
+  subprocess.check_call([git, 'checkout', '--quiet', commithash], cwd=directory)
 
   if verbose:
-    status(directory, checkoutable)  # Success.
+    status(directory, commithash, True)  # Success.
 
 
 def parse_file_to_dict(path):
@@ -171,6 +176,11 @@
   return dictionary
 
 
+def is_sha1_sum(s):
+  """SHA1 sums are 160 bits, encoded as lowercase hexadecimal."""
+  return len(s) == 40 and all(c in '0123456789abcdef' for c in s)
+
+
 def git_sync_deps(deps_file_path, command_line_os_requests, verbose):
   """Grab dependencies, with optional platform support.
 
@@ -209,14 +219,16 @@
         print 'Skipping "%s".' % directory
       continue
     if '@' in dependencies[directory]:
-      repo, checkoutable = dependencies[directory].split('@', 1)
+      repo, commithash = dependencies[directory].split('@', 1)
     else:
-      raise Exception("please specify commit or tag")
+      raise Exception("please specify commit")
+    if not is_sha1_sum(commithash):
+      raise Exception("poorly formed commit hash: %r" % commithash)
 
     relative_directory = os.path.join(deps_file_directory, directory)
 
     list_of_arg_lists.append(
-      (git, repo, checkoutable, relative_directory, verbose))
+      (git, repo, commithash, relative_directory, verbose))
 
   multithread(git_checkout_to_directory, list_of_arg_lists)
 
diff --git a/tools/gpu/GrContextFactory.cpp b/tools/gpu/GrContextFactory.cpp
index d0defdc..be33802 100644
--- a/tools/gpu/GrContextFactory.cpp
+++ b/tools/gpu/GrContextFactory.cpp
@@ -8,7 +8,9 @@
 
 #include "src/gpu/GrContextPriv.h"
 #include "tools/gpu/GrContextFactory.h"
+#ifdef SK_GL
 #include "tools/gpu/gl/GLTestContext.h"
+#endif
 
 #if SK_ANGLE
     #include "tools/gpu/gl/angle/GLTestContext_angle.h"
@@ -152,6 +154,7 @@
     std::unique_ptr<TestContext> testCtx;
     GrBackendApi backend = ContextTypeBackend(type);
     switch (backend) {
+#ifdef SK_GL
         case GrBackendApi::kOpenGL: {
             GLTestContext* glShareContext = masterContext
                     ? static_cast<GLTestContext*>(masterContext->fTestContext) : nullptr;
@@ -199,6 +202,7 @@
             testCtx.reset(glCtx);
             break;
         }
+#endif  // SK_GL
 #ifdef SK_VULKAN
         case GrBackendApi::kVulkan: {
             VkTestContext* vkSharedContext = masterContext
@@ -209,6 +213,7 @@
                 return ContextInfo();
             }
 
+#ifdef SK_GL
             // There is some bug (either in Skia or the NV Vulkan driver) where VkDevice
             // destruction will hang occaisonally. For some reason having an existing GL
             // context fixes this.
@@ -218,6 +223,7 @@
                     fSentinelGLContext.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
                 }
             }
+#endif
             break;
         }
 #endif
diff --git a/tools/gpu/ProxyUtils.cpp b/tools/gpu/ProxyUtils.cpp
index a4cf52d..09465e1 100644
--- a/tools/gpu/ProxyUtils.cpp
+++ b/tools/gpu/ProxyUtils.cpp
@@ -5,11 +5,13 @@
  * found in the LICENSE file.
  */
 
+#include "include/core/SkColor.h"
 #include "include/gpu/GrBackendSurface.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrDrawingManager.h"
 #include "src/gpu/GrGpu.h"
 #include "src/gpu/GrImageInfo.h"
+#include "src/gpu/GrProgramInfo.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/SkGr.h"
 #include "tools/gpu/ProxyUtils.h"
@@ -56,4 +58,56 @@
     return proxy;
 }
 
+GrProgramInfo* CreateProgramInfo(const GrCaps* caps,
+                                 SkArenaAlloc* arena,
+                                 const GrSurfaceProxyView* dstView,
+                                 GrAppliedClip&& appliedClip,
+                                 const GrXferProcessor::DstProxyView& dstProxyView,
+                                 GrGeometryProcessor* geomProc,
+                                 SkBlendMode blendMode,
+                                 GrPrimitiveType primitiveType,
+                                 GrPipeline::InputFlags flags,
+                                 const GrUserStencilSettings* stencil) {
+
+    GrPipeline::InitArgs initArgs;
+    initArgs.fInputFlags = flags;
+    initArgs.fUserStencil = stencil;
+    initArgs.fCaps = caps;
+    initArgs.fDstProxyView = dstProxyView;
+    initArgs.fOutputSwizzle = dstView->swizzle();
+
+    GrPipeline::FixedDynamicState* fixedDynamicState = nullptr;
+
+    if (appliedClip.scissorState().enabled()) {
+        fixedDynamicState = arena->make<GrPipeline::FixedDynamicState>(
+                                                        appliedClip.scissorState().rect());
+    }
+
+    GrProcessorSet processors = GrProcessorSet(blendMode);
+
+    SkPMColor4f analysisColor = { 0, 0, 0, 1 }; // opaque black
+
+    SkDEBUGCODE(auto analysis =) processors.finalize(analysisColor,
+                                                     GrProcessorAnalysisCoverage::kNone,
+                                                     &appliedClip, stencil, false,
+                                                     *caps, GrClampType::kAuto, &analysisColor);
+    SkASSERT(!analysis.requiresDstTexture());
+
+    GrPipeline* pipeline = arena->make<GrPipeline>(initArgs,
+                                                   std::move(processors),
+                                                   std::move(appliedClip));
+
+    GrRenderTargetProxy* dstProxy = dstView->asRenderTargetProxy();
+    return arena->make<GrProgramInfo>(dstProxy->numSamples(),
+                                      dstProxy->numStencilSamples(),
+                                      dstProxy->backendFormat(),
+                                      dstView->origin(),
+                                      pipeline,
+                                      geomProc,
+                                      fixedDynamicState,
+                                      nullptr, 0,
+                                      primitiveType);
+}
+
+
 }  // namespace sk_gpu_test
diff --git a/tools/gpu/ProxyUtils.h b/tools/gpu/ProxyUtils.h
index ed4c625..28bf011 100644
--- a/tools/gpu/ProxyUtils.h
+++ b/tools/gpu/ProxyUtils.h
@@ -10,8 +10,11 @@
 
 #include "include/private/GrTypesPriv.h"
 #include "src/gpu/GrImageInfo.h"
+#include "src/gpu/GrPipeline.h"
 #include "src/gpu/GrTextureProxy.h"
 
+class GrProgramInfo;
+
 namespace sk_gpu_test {
 
 /** Makes a texture proxy containing the passed in color data. */
@@ -22,6 +25,18 @@
                                                const void* data,
                                                size_t rowBytes);
 
+GrProgramInfo* CreateProgramInfo(const GrCaps*,
+                                 SkArenaAlloc*,
+                                 const GrSurfaceProxyView* dstView,
+                                 GrAppliedClip&&,
+                                 const GrXferProcessor::DstProxyView& dstProxyView,
+                                 GrGeometryProcessor*, SkBlendMode,
+                                 GrPrimitiveType,
+                                 GrPipeline::InputFlags flags = GrPipeline::InputFlags::kNone,
+                                 const GrUserStencilSettings* stencil =
+                                                                &GrUserStencilSettings::kUnused);
+
+
 }  // namespace sk_gpu_test
 
 #endif
diff --git a/tools/gpu/TestOps.cpp b/tools/gpu/TestOps.cpp
new file mode 100644
index 0000000..22a8a66
--- /dev/null
+++ b/tools/gpu/TestOps.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/gpu/TestOps.h"
+
+#include "src/core/SkPointPriv.h"
+#include "src/gpu/GrCaps.h"
+#include "src/gpu/GrGeometryProcessor.h"
+#include "src/gpu/GrMemoryPool.h"
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrVertexWriter.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
+#include "src/gpu/glsl/GrGLSLVarying.h"
+#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
+
+namespace {
+
+class GP : public GrGeometryProcessor {
+public:
+    GP(const SkMatrix& localMatrix, bool wideColor)
+            : GrGeometryProcessor(kTestRectOp_ClassID), fLocalMatrix(localMatrix) {
+        fInColor = MakeColorAttribute("color", wideColor);
+        this->setVertexAttributes(&fInPosition, 3);
+    }
+
+    const char* name() const override { return "TestRectOp::GP"; }
+
+    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override;
+
+    void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
+
+    bool wideColor() const { return fInColor.cpuType() != kUByte4_norm_GrVertexAttribType; }
+
+private:
+    Attribute fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
+    Attribute fInLocalCoords = {"inLocalCoords", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
+    Attribute fInColor;
+    SkMatrix fLocalMatrix;
+};
+
+GrGLSLPrimitiveProcessor* GP::createGLSLInstance(const GrShaderCaps& caps) const {
+    class GLSLGP : public GrGLSLGeometryProcessor {
+        void setData(const GrGLSLProgramDataManager& pdman,
+                     const GrPrimitiveProcessor& pp,
+                     const CoordTransformRange& transformRange) override {
+            const auto& gp = pp.cast<GP>();
+            this->setTransformDataHelper(gp.fLocalMatrix, pdman, transformRange);
+        }
+
+    private:
+        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
+            const auto& gp = args.fGP.cast<GP>();
+            args.fVaryingHandler->emitAttributes(gp);
+            GrGLSLVarying colorVarying(kHalf4_GrSLType);
+            args.fVaryingHandler->addVarying("color", &colorVarying,
+                                             GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
+            args.fVertBuilder->codeAppendf("%s = %s;", colorVarying.vsOut(), gp.fInColor.name());
+            args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, colorVarying.fsIn());
+            args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
+            this->writeOutputPosition(args.fVertBuilder, gpArgs, gp.fInPosition.name());
+            this->emitTransforms(args.fVertBuilder, args.fVaryingHandler, args.fUniformHandler,
+                                 gp.fInLocalCoords.asShaderVar(), gp.fLocalMatrix,
+                                 args.fFPCoordTransformHandler);
+        }
+    };
+    return new GLSLGP();
+}
+
+class TestRectOp final : public GrMeshDrawOp {
+public:
+    static std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
+                                          GrPaint&&,
+                                          const SkRect& drawRect,
+                                          const SkRect& localRect,
+                                          const SkMatrix& localM);
+
+    const char* name() const override { return "TestRectOp"; }
+
+    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
+
+    GrProcessorSet::Analysis finalize(const GrCaps&,
+                                      const GrAppliedClip*,
+                                      bool hasMixedSampledCoverage,
+                                      GrClampType) override;
+
+    void visitProxies(const VisitProxyFunc& func) const override {
+        fProcessorSet.visitProxies(func);
+    }
+
+private:
+    DEFINE_OP_CLASS_ID
+
+    TestRectOp(const GrCaps*,
+               GrPaint&&,
+               const SkRect& drawRect,
+               const SkRect& localRect,
+               const SkMatrix& localMatrix);
+    void onPrepareDraws(Target*) override;
+    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
+
+    SkRect fDrawRect;
+    SkRect fLocalRect;
+    SkPMColor4f fColor;
+    GP fGP;
+    GrProcessorSet fProcessorSet;
+
+    friend class ::GrOpMemoryPool;
+};
+
+std::unique_ptr<GrDrawOp> TestRectOp::Make(GrRecordingContext* context,
+                                           GrPaint&& paint,
+                                           const SkRect& drawRect,
+                                           const SkRect& localRect,
+                                           const SkMatrix& localM) {
+    auto* pool = context->priv().opMemoryPool();
+    const auto* caps = context->priv().caps();
+    return pool->allocate<TestRectOp>(caps, std::move(paint), drawRect, localRect, localM);
+}
+
+GrProcessorSet::Analysis TestRectOp::finalize(const GrCaps& caps,
+                                              const GrAppliedClip* clip,
+                                              bool hasMixedSampledCoverage,
+                                              GrClampType clampType) {
+    return fProcessorSet.finalize(GrProcessorAnalysisColor::Opaque::kYes,
+                                  GrProcessorAnalysisCoverage::kSingleChannel, clip,
+                                  &GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps,
+                                  clampType, &fColor);
+}
+
+static bool use_wide_color(const GrPaint& paint, const GrCaps* caps) {
+    return !paint.getColor4f().fitsInBytes() && caps->halfFloatVertexAttributeSupport();
+}
+TestRectOp::TestRectOp(const GrCaps* caps,
+                       GrPaint&& paint,
+                       const SkRect& drawRect,
+                       const SkRect& localRect,
+                       const SkMatrix& localMatrix)
+        : GrMeshDrawOp(ClassID())
+        , fDrawRect(drawRect)
+        , fLocalRect(localRect)
+        , fColor(paint.getColor4f())
+        , fGP(localMatrix, use_wide_color(paint, caps))
+        , fProcessorSet(std::move(paint)) {
+    this->setBounds(drawRect.makeSorted(), HasAABloat::kNo, IsHairline::kNo);
+}
+
+void TestRectOp::onPrepareDraws(Target* target) {
+    QuadHelper helper(target, fGP.vertexStride(), 1);
+    GrVertexWriter writer{helper.vertices()};
+    auto pos = GrVertexWriter::TriStripFromRect(fDrawRect);
+    auto local = GrVertexWriter::TriStripFromRect(fLocalRect);
+    GrVertexColor color(fColor, fGP.wideColor());
+    writer.writeQuad(pos, local, color);
+    helper.recordDraw(target, &fGP);
+}
+
+void TestRectOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
+    flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, std::move(fProcessorSet));
+}
+
+}  // anonymous namespace
+
+namespace sk_gpu_test::test_ops {
+
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext* context,
+                                   GrPaint&& paint,
+                                   const SkRect& drawRect,
+                                   const SkRect& localRect,
+                                   const SkMatrix& localM) {
+    return TestRectOp::Make(context, std::move(paint), drawRect, localRect, localM);
+}
+
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext* context,
+                                   std::unique_ptr<GrFragmentProcessor> fp,
+                                   const SkRect& drawRect,
+                                   const SkRect& localRect,
+                                   const SkMatrix& localM) {
+    GrPaint paint;
+    paint.addColorFragmentProcessor(std::move(fp));
+    return TestRectOp::Make(context, std::move(paint), drawRect, localRect, localM);
+}
+
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext* context,
+                                   GrPaint&& paint,
+                                   const SkRect& rect) {
+    return TestRectOp::Make(context, std::move(paint), rect, rect, SkMatrix::I());
+}
+
+}  // namespace sk_gpu_test::test_ops
diff --git a/tools/gpu/TestOps.h b/tools/gpu/TestOps.h
new file mode 100644
index 0000000..9f05a32
--- /dev/null
+++ b/tools/gpu/TestOps.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef TestRectOp_DEFINED
+#define TestRectOp_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/private/GrRecordingContext.h"
+#include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/ops/GrMeshDrawOp.h"
+
+class GrPaint;
+
+namespace sk_gpu_test::test_ops {
+
+/**
+ * Fully specified device space rect op. The test Op draws a rectangle with local coords and a
+ * local matrix. It is important to test effects in the presence of GP local matrices. Our standard
+ * rect drawing code doesn't exercise this because it applies any local matrix to pre-transformed
+ * local coord vertex attributes.
+ */
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext*,
+                                   GrPaint&&,
+                                   const SkRect& drawRect,
+                                   const SkRect& localRect,
+                                   const SkMatrix& localM = SkMatrix::I());
+
+/**
+ * A simpler version of MakeRect that takes a single color FP instead of a full paint. Uses
+ * SkBlendMode::kSrcOver.
+ */
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext*,
+                                   std::unique_ptr<GrFragmentProcessor>,
+                                   const SkRect& drawRect,
+                                   const SkRect& localRect,
+                                   const SkMatrix& localM = SkMatrix::I());
+
+/**
+ * A simpler version of MakeRect that uses the same rect as the device space rect to draw as well as
+ * the local rect. The local matrix is identity.
+ */
+std::unique_ptr<GrDrawOp> MakeRect(GrRecordingContext*, GrPaint&&, const SkRect& rect);
+
+}  // namespace sk_gpu_test::test_ops
+
+#endif
diff --git a/tools/gpu/dawn/DawnTestContext.cpp b/tools/gpu/dawn/DawnTestContext.cpp
index 97ed871..5ab7af9 100644
--- a/tools/gpu/dawn/DawnTestContext.cpp
+++ b/tools/gpu/dawn/DawnTestContext.cpp
@@ -80,7 +80,7 @@
 
 class DawnFence {
 public:
-    DawnFence(const dawn::Device& device, const dawn::Buffer& buffer)
+    DawnFence(const wgpu::Device& device, const wgpu::Buffer& buffer)
       : fDevice(device), fBuffer(buffer), fCalled(false) {
         fBuffer.MapReadAsync(callback, this);
     }
@@ -100,11 +100,11 @@
         DawnFence* fence = static_cast<DawnFence*>(userData);
         fence->fCalled = true;
     }
-    dawn::Buffer buffer() { return fBuffer; }
+    wgpu::Buffer buffer() { return fBuffer; }
 
 private:
-    dawn::Device                   fDevice;
-    dawn::Buffer                   fBuffer;
+    wgpu::Device                   fDevice;
+    wgpu::Buffer                   fBuffer;
     bool                           fCalled;
 };
 
@@ -113,17 +113,17 @@
  */
 class DawnFenceSync : public sk_gpu_test::FenceSync {
 public:
-    DawnFenceSync(dawn::Device device) : fDevice(device) {
+    DawnFenceSync(wgpu::Device device) : fDevice(device) {
     }
 
     ~DawnFenceSync() override {
     }
 
     sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override {
-        dawn::Buffer buffer;
+        wgpu::Buffer buffer;
         if (fBuffers.empty()) {
-            dawn::BufferDescriptor desc;
-            desc.usage = dawn::BufferUsage::MapRead | dawn::BufferUsage::CopyDst;
+            wgpu::BufferDescriptor desc;
+            desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
             desc.size = 1;
             buffer = fDevice.CreateBuffer(&desc);
         } else {
@@ -147,15 +147,15 @@
     }
 
 private:
-    dawn::Device                      fDevice;
-    mutable std::vector<dawn::Buffer> fBuffers;
+    wgpu::Device                      fDevice;
+    mutable std::vector<wgpu::Buffer> fBuffers;
     mutable AutoreleasePool           fAutoreleasePool;
     typedef sk_gpu_test::FenceSync INHERITED;
 };
 
 class DawnTestContextImpl : public sk_gpu_test::DawnTestContext {
 public:
-    static dawn::Device createDevice(const dawn_native::Instance& instance,
+    static wgpu::Device createDevice(const dawn_native::Instance& instance,
                                      dawn_native::BackendType type) {
         DawnProcTable backendProcs = dawn_native::GetProcs();
         dawnProcSetProcs(&backendProcs);
@@ -171,7 +171,7 @@
 
     static DawnTestContext* Create(DawnTestContext* sharedContext) {
         std::unique_ptr<dawn_native::Instance> instance = std::make_unique<dawn_native::Instance>();
-        dawn::Device device;
+        wgpu::Device device;
         if (sharedContext) {
             device = sharedContext->getDevice();
         } else {
@@ -227,7 +227,7 @@
 
 private:
     DawnTestContextImpl(std::unique_ptr<dawn_native::Instance> instance,
-                        const dawn::Device& device)
+                        const wgpu::Device& device)
             : DawnTestContext(device)
             , fInstance(std::move(instance)) {
         fFenceSync.reset(new DawnFenceSync(fDevice));
diff --git a/tools/gpu/dawn/DawnTestContext.h b/tools/gpu/dawn/DawnTestContext.h
index 4afd018..63f35f6 100644
--- a/tools/gpu/dawn/DawnTestContext.h
+++ b/tools/gpu/dawn/DawnTestContext.h
@@ -17,14 +17,14 @@
 public:
     virtual GrBackend backend() override { return GrBackendApi::kDawn; }
 
-    const dawn::Device& getDevice() {
+    const wgpu::Device& getDevice() {
         return fDevice;
     }
 
 protected:
-    DawnTestContext(const dawn::Device& device) : fDevice(device) {}
+    DawnTestContext(const wgpu::Device& device) : fDevice(device) {}
 
-    dawn::Device fDevice;
+    wgpu::Device fDevice;
 
 private:
     typedef TestContext INHERITED;
diff --git a/tools/gpu/vk/GrVulkanDefines.h b/tools/gpu/vk/GrVulkanDefines.h
index ad65a84..739bdc7 100644
--- a/tools/gpu/vk/GrVulkanDefines.h
+++ b/tools/gpu/vk/GrVulkanDefines.h
@@ -19,7 +19,7 @@
 #      define VK_USE_PLATFORM_ANDROID_KHR
 #   endif
 #elif defined(SK_BUILD_FOR_UNIX)
-#   if !defined(VK_USE_PLATFORM_XCB_KHR)
+#   if !defined(__Fuchsia__) && !defined(VK_USE_PLATFORM_XCB_KHR)
 #      define VK_USE_PLATFORM_XCB_KHR
 #   endif
 #elif defined(SK_BUILD_FOR_MAC)
diff --git a/tools/infra/git.py b/tools/infra/git.py
old mode 100644
new mode 100755
diff --git a/tools/infra/go.py b/tools/infra/go.py
old mode 100644
new mode 100755
diff --git a/tools/rebaseline/toggle_legacy_flag.py b/tools/rebaseline/toggle_legacy_flag.py
old mode 100644
new mode 100755
diff --git a/tools/retrieve_from_googlesource.py b/tools/retrieve_from_googlesource.py
old mode 100644
new mode 100755
diff --git a/tools/rewrite_includes.py b/tools/rewrite_includes.py
old mode 100644
new mode 100755
diff --git a/tools/sk_app/DawnWindowContext.cpp b/tools/sk_app/DawnWindowContext.cpp
index 72cd006..9bc9de0 100644
--- a/tools/sk_app/DawnWindowContext.cpp
+++ b/tools/sk_app/DawnWindowContext.cpp
@@ -13,8 +13,8 @@
 
 #include "dawn/dawn_proc.h"
 
-static dawn::TextureUsage kUsage = dawn::TextureUsage::OutputAttachment |
-                                   dawn::TextureUsage::CopySrc;
+static wgpu::TextureUsage kUsage = wgpu::TextureUsage::OutputAttachment |
+                                   wgpu::TextureUsage::CopySrc;
 
 static void PrintDeviceError(DawnErrorType, const char* message, void*) {
     printf("Device error: %s\n", message);
@@ -24,7 +24,7 @@
 namespace sk_app {
 
 DawnWindowContext::DawnWindowContext(const DisplayParams& params,
-                                     dawn::TextureFormat swapChainFormat)
+                                     wgpu::TextureFormat swapChainFormat)
     : WindowContext(params)
     , fSwapChainFormat(swapChainFormat)
     , fInstance(std::make_unique<dawn_native::Instance>()) {
@@ -40,7 +40,7 @@
         return;
     }
     fSwapChainImplementation = this->createSwapChainImplementation(-1, -1, fDisplayParams);
-    dawn::SwapChainDescriptor swapChainDesc;
+    wgpu::SwapChainDescriptor swapChainDesc;
     swapChainDesc.implementation = reinterpret_cast<int64_t>(&fSwapChainImplementation);
     fSwapChain = fDevice.CreateSwapChain(&swapChainDesc);
     if (!fSwapChain) {
@@ -95,7 +95,7 @@
     fWidth = w;
     fHeight = h;
     fSwapChainImplementation = this->createSwapChainImplementation(w, h, fDisplayParams);
-    dawn::SwapChainDescriptor swapChainDesc;
+    wgpu::SwapChainDescriptor swapChainDesc;
     swapChainDesc.implementation = reinterpret_cast<int64_t>(&fSwapChainImplementation);
     fSwapChain = fDevice.CreateSwapChain(&swapChainDesc);
     if (!fSwapChain) {
@@ -109,7 +109,7 @@
     fDisplayParams = params;
 }
 
-dawn::Device DawnWindowContext::createDevice(dawn_native::BackendType type) {
+wgpu::Device DawnWindowContext::createDevice(dawn_native::BackendType type) {
     fInstance->DiscoverDefaultAdapters();
     DawnProcTable backendProcs = dawn_native::GetProcs();
     dawnProcSetProcs(&backendProcs);
diff --git a/tools/sk_app/DawnWindowContext.h b/tools/sk_app/DawnWindowContext.h
index 6c2e1c9..e762c18 100644
--- a/tools/sk_app/DawnWindowContext.h
+++ b/tools/sk_app/DawnWindowContext.h
@@ -21,7 +21,7 @@
 
 class DawnWindowContext : public WindowContext {
 public:
-    DawnWindowContext(const DisplayParams&, dawn::TextureFormat swapChainFormat);
+    DawnWindowContext(const DisplayParams&, wgpu::TextureFormat swapChainFormat);
     ~DawnWindowContext() override;
     sk_sp<SkSurface> getBackbufferSurface() override;
     void swapBuffers() override;
@@ -34,8 +34,8 @@
 protected:
     bool isGpuContext() override { return true; }
     void initializeContext(int width, int height);
-    dawn::Device createDevice(dawn_native::BackendType type);
-    virtual dawn::Device onInitializeContext() = 0;
+    wgpu::Device createDevice(dawn_native::BackendType type);
+    virtual wgpu::Device onInitializeContext() = 0;
     virtual void onDestroyContext() = 0;
     virtual void onSwapBuffers() = 0;
     virtual GrSurfaceOrigin getRTOrigin() const { return kTopLeft_GrSurfaceOrigin; }
@@ -45,9 +45,9 @@
 
     sk_sp<SkSurface>              fSurface;
     DawnSwapChainImplementation   fSwapChainImplementation;
-    dawn::TextureFormat           fSwapChainFormat;
-    dawn::SwapChain               fSwapChain;
-    dawn::Device                  fDevice;
+    wgpu::TextureFormat           fSwapChainFormat;
+    wgpu::SwapChain               fSwapChain;
+    wgpu::Device                  fDevice;
     std::unique_ptr<dawn_native::Instance> fInstance;
 };
 
diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
index 793c88c..c2b26b4 100644
--- a/tools/sk_app/VulkanWindowContext.cpp
+++ b/tools/sk_app/VulkanWindowContext.cpp
@@ -372,9 +372,10 @@
     fBackbuffers = new BackbufferInfo[fImageCount + 1];
     for (uint32_t i = 0; i < fImageCount + 1; ++i) {
         fBackbuffers[i].fImageIndex = -1;
-        GR_VK_CALL_ERRCHECK(fInterface,
-                            CreateSemaphore(fDevice, &semaphoreInfo,
-                                            nullptr, &fBackbuffers[i].fRenderSemaphore));
+        SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface,
+                CreateSemaphore(fDevice, &semaphoreInfo, nullptr,
+                                &fBackbuffers[i].fRenderSemaphore));
+        SkASSERT(result == VK_SUCCESS);
     }
     fCurrentBackbufferIndex = fImageCount;
 }
@@ -470,8 +471,9 @@
     semaphoreInfo.pNext = nullptr;
     semaphoreInfo.flags = 0;
     VkSemaphore semaphore;
-    GR_VK_CALL_ERRCHECK(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
-                                                    nullptr, &semaphore));
+    SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
+                                                                          nullptr, &semaphore));
+    SkASSERT(result == VK_SUCCESS);
 
     // acquire the image
     VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
diff --git a/tools/sk_app/ios/main_ios.mm b/tools/sk_app/ios/main_ios.mm
index 3bcc236..94b3a7b 100644
--- a/tools/sk_app/ios/main_ios.mm
+++ b/tools/sk_app/ios/main_ios.mm
@@ -48,6 +48,9 @@
         ++i;
     }
     argv[i] = NULL;
+    [arguments release];
+
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
     Application* app = Application::Create(argc, argv, nullptr);
 
@@ -73,6 +76,9 @@
             result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, kSeconds, TRUE);
         } while (result == kCFRunLoopRunHandledSource);
 
+        [pool drain];
+        pool = [[NSAutoreleasePool alloc] init];
+
         // TODO: is this the right approach for iOS?
         // Rather than depending on an iOS event to drive this, we treat our window
         // invalidation flag as a separate event stream. Window::onPaint() will clear
diff --git a/tools/sk_app/mac/DawnMTLWindowContext_mac.mm b/tools/sk_app/mac/DawnMTLWindowContext_mac.mm
index 2ce22f3..651b044 100644
--- a/tools/sk_app/mac/DawnMTLWindowContext_mac.mm
+++ b/tools/sk_app/mac/DawnMTLWindowContext_mac.mm
@@ -25,7 +25,7 @@
 public:
     DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params);
     ~DawnMTLWindowContext() override;
-    dawn::Device onInitializeContext() override;
+    wgpu::Device onInitializeContext() override;
     void onDestroyContext() override;
     DawnSwapChainImplementation createSwapChainImplementation(int width, int height,
                                                               const DisplayParams& params) override;
@@ -84,7 +84,7 @@
 };
 
 DawnMTLWindowContext::DawnMTLWindowContext(const MacWindowInfo& info, const DisplayParams& params)
-    : DawnWindowContext(params, dawn::TextureFormat::BGRA8Unorm)
+    : DawnWindowContext(params, wgpu::TextureFormat::BGRA8Unorm)
     , fMainView(info.fMainView) {
     CGSize size = fMainView.bounds.size;
     this->initializeContext(size.width, size.height);
@@ -99,8 +99,8 @@
     return SwapChainImplMTL::Create(fMTLDevice, fLayer);
 }
 
-dawn::Device DawnMTLWindowContext::onInitializeContext() {
-    dawn::Device device = this->createDevice(dawn_native::BackendType::Metal);
+wgpu::Device DawnMTLWindowContext::onInitializeContext() {
+    wgpu::Device device = this->createDevice(dawn_native::BackendType::Metal);
     if (!device) {
         return nullptr;
     }
diff --git a/tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp b/tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp
index b8eee27..b266e54 100644
--- a/tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp
+++ b/tools/sk_app/unix/DawnVulkanWindowContext_unix.cpp
@@ -25,7 +25,7 @@
 public:
     DawnVulkanWindowContext_xlib(const XlibWindowInfo&, const DisplayParams&);
     ~DawnVulkanWindowContext_xlib() override {}
-    dawn::Device onInitializeContext() override;
+    wgpu::Device onInitializeContext() override;
     void onDestroyContext() override {}
     DawnSwapChainImplementation createSwapChainImplementation(
             int width, int height, const DisplayParams& params) override;
@@ -41,7 +41,7 @@
 
 DawnVulkanWindowContext_xlib::DawnVulkanWindowContext_xlib(const XlibWindowInfo& winInfo,
                                                            const DisplayParams& params)
-        : INHERITED(params, dawn::TextureFormat::BGRA8Unorm)
+        : INHERITED(params, wgpu::TextureFormat::BGRA8Unorm)
         , fDisplay(winInfo.fDisplay)
         , fWindow(winInfo.fWindow) {
     XWindow root;
@@ -57,8 +57,8 @@
     return dawn_native::vulkan::CreateNativeSwapChainImpl(fDevice.Get(), fVkSurface);
 }
 
-dawn::Device DawnVulkanWindowContext_xlib::onInitializeContext() {
-    dawn::Device device = this->createDevice(dawn_native::BackendType::Vulkan);
+wgpu::Device DawnVulkanWindowContext_xlib::onInitializeContext() {
+    wgpu::Device device = this->createDevice(dawn_native::BackendType::Vulkan);
     if (!device) {
         return nullptr;
     }
diff --git a/tools/sk_app/win/DawnD3D12WindowContext_win.cpp b/tools/sk_app/win/DawnD3D12WindowContext_win.cpp
index b5e8599..ee39d1f 100644
--- a/tools/sk_app/win/DawnD3D12WindowContext_win.cpp
+++ b/tools/sk_app/win/DawnD3D12WindowContext_win.cpp
@@ -18,8 +18,8 @@
 class DawnD3D12WindowContext : public DawnWindowContext {
 public:
     DawnD3D12WindowContext(HWND hwnd, const DisplayParams& params);
-    virtual ~DawnD3D12WindowContext();
-    dawn::Device onInitializeContext() override;
+    ~DawnD3D12WindowContext() override;
+    wgpu::Device onInitializeContext() override;
     void onDestroyContext() override;
     DawnSwapChainImplementation createSwapChainImplementation(
             int width, int height, const DisplayParams& params) override;
@@ -30,7 +30,7 @@
 
 // NOTE: this texture format must match the one in D3D12's swap chain impl
 DawnD3D12WindowContext::DawnD3D12WindowContext(HWND hwnd, const DisplayParams& params)
-    : DawnWindowContext(params, dawn::TextureFormat::RGBA8Unorm)
+    : DawnWindowContext(params, wgpu::TextureFormat::RGBA8Unorm)
     , fWindow(hwnd) {
     RECT rect;
     GetClientRect(hwnd, &rect);
@@ -46,7 +46,7 @@
     return dawn_native::d3d12::CreateNativeSwapChainImpl(fDevice.Get(), fWindow);
 }
 
-dawn::Device DawnD3D12WindowContext::onInitializeContext() {
+wgpu::Device DawnD3D12WindowContext::onInitializeContext() {
     return this->createDevice(dawn_native::BackendType::D3D12);
 }
 
diff --git a/tools/skottie-wasm-perf/parse_perf_csvs.py b/tools/skottie-wasm-perf/parse_perf_csvs.py
old mode 100644
new mode 100755
diff --git a/tools/skottie2movie.cpp b/tools/skottie2movie.cpp
index e12ad3c..ca887b5 100644
--- a/tools/skottie2movie.cpp
+++ b/tools/skottie2movie.cpp
@@ -12,7 +12,7 @@
 #include "include/core/SkSurface.h"
 #include "include/core/SkTime.h"
 #include "modules/skottie/include/Skottie.h"
-#include "modules/skottie/utils/SkottieUtils.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/utils/SkOSPath.h"
 
 #include "tools/flags/CommandLineFlags.h"
@@ -29,8 +29,8 @@
 static DEFINE_int(set_dst_width, 0, "set destination width (height will be computed)");
 static DEFINE_bool2(gpu, g, false, "use GPU for rendering");
 
-static void produce_frame(SkSurface* surf, skottie::Animation* anim, double frame_time) {
-    anim->seekFrameTime(frame_time);
+static void produce_frame(SkSurface* surf, skottie::Animation* anim, double frame) {
+    anim->seekFrame(frame);
     surf->getCanvas()->clear(SK_ColorWHITE);
     anim->render(surf->getCanvas());
 }
@@ -64,7 +64,7 @@
     SkDebugf("assetPath %s\n", assetPath.c_str());
 
     auto animation = skottie::Animation::Builder()
-        .setResourceProvider(skottie_utils::FileResourceProvider::Make(assetPath))
+        .setResourceProvider(skresources::FileResourceProvider::Make(assetPath))
         .makeFromFile(FLAGS_input[0]);
     if (!animation) {
         SkDebugf("failed to load %s\n", FLAGS_input[0]);
@@ -73,12 +73,8 @@
 
     SkISize dim = animation->size().toRound();
     double duration = animation->duration();
-    int fps = FLAGS_fps;
-    if (fps < 1) {
-        fps = 1;
-    } else if (fps > 240) {
-        fps = 240;
-    }
+    int fps = SkTPin(FLAGS_fps, 1, 240);
+    double fps_scale = animation->fps() / fps;
 
     float scale = 1;
     if (FLAGS_set_dst_width > 0) {
@@ -130,24 +126,26 @@
         }
 
         for (int i = 0; i <= frames; ++i) {
-            double ts = i * 1.0 / fps;
+            const double frame = i * fps_scale;
             if (FLAGS_verbose) {
-                SkDebugf("rendering frame %d ts %g\n", i, ts);
+                SkDebugf("rendering frame %g\n", frame);
             }
 
-            double normal_time = i * 1.0 / frames;
-            double frame_time = normal_time * duration;
-
-            produce_frame(surf.get(), animation.get(), frame_time);
+            produce_frame(surf.get(), animation.get(), frame);
 
             AsyncRec asyncRec = { info, &encoder };
             if (context) {
+                auto read_pixels_cb = [](SkSurface::ReadPixelsContext ctx,
+                                         std::unique_ptr<const SkSurface::AsyncReadResult> result) {
+                    if (result && result->count() == 1) {
+                        AsyncRec* rec = reinterpret_cast<AsyncRec*>(ctx);
+                        rec->encoder->addFrame({rec->info, result->data(0), result->rowBytes(0)});
+                    }
+                };
                 surf->asyncRescaleAndReadPixels(info, {0, 0, info.width(), info.height()},
-                                                SkSurface::RescaleGamma::kSrc, kNone_SkFilterQuality,
-                                                [](void* ctx, const void* data, size_t rb) {
-                    AsyncRec* rec = (AsyncRec*)ctx;
-                    rec->encoder->addFrame({rec->info, data, rb});
-                }, &asyncRec);
+                                                SkSurface::RescaleGamma::kSrc,
+                                                kNone_SkFilterQuality,
+                                                read_pixels_cb, &asyncRec);
             } else {
                 SkPixmap pm;
                 SkAssertResult(surf->peekPixels(&pm));
diff --git a/tools/skp/generate_page_set.py b/tools/skp/generate_page_set.py
old mode 100644
new mode 100755
diff --git a/tools/skp/webpages_playback.py b/tools/skp/webpages_playback.py
old mode 100644
new mode 100755
diff --git a/tools/skpbench/skiaperf.py b/tools/skpbench/skiaperf.py
index 8e4f837..23608a5 100755
--- a/tools/skpbench/skiaperf.py
+++ b/tools/skpbench/skiaperf.py
@@ -77,7 +77,7 @@
           continue
         if match.sample_ms != 50:
           raise Exception("%s: unexpected sample_ms != 50" % match.sample_ms)
-        for result in ('accum', 'median'):
+        for result in ('accum', 'median', 'min', 'max'):
           data['results'][match.bench][match.config] \
               ['%s_%s_%s' % (result, match.clock, match.metric)] = \
               getattr(match, result)
diff --git a/tools/skpbench/skpbench.py b/tools/skpbench/skpbench.py
index 7e40df4..f739677 100755
--- a/tools/skpbench/skpbench.py
+++ b/tools/skpbench/skpbench.py
@@ -51,6 +51,9 @@
   type=int, help="number of milliseconds to run each benchmark")
 __argparse.add_argument('-l', '--sample-ms',
   type=int, help="duration of a sample (minimum)")
+__argparse.add_argument('--force',
+  action='store_true',
+  help="perform benchmarking on unrecognized Android devices")
 __argparse.add_argument('--gpu',
   action='store_true',
   help="perform timing on the gpu clock instead of cpu (gpu work only)")
@@ -345,11 +348,14 @@
     elif model == 'Nexus 6P':
       from _hardware_nexus_6p import HardwareNexus6P
       hardware = HardwareNexus6P(adb)
-    else:
+    elif FLAGS.force:
       from _hardware_android import HardwareAndroid
       print("WARNING: %s: don't know how to monitor this hardware; results "
             "may be unreliable." % model, file=sys.stderr)
       hardware = HardwareAndroid(adb)
+    else:
+      raise Exception("%s: don't know how to monitor this hardware. "
+                      "Use --force to bypass this warning." % model)
   else:
     hardware = Hardware()
 
diff --git a/tools/skqp/create_apk.py b/tools/skqp/create_apk.py
index 2f6d94e..27bff79 100755
--- a/tools/skqp/create_apk.py
+++ b/tools/skqp/create_apk.py
@@ -150,7 +150,11 @@
             gn_args = opts.gn_args(arch)
             args = ' '.join('%s=%s' % (k, v) for k, v in gn_args.items())
             check_call(['bin/gn', 'gen', build, '--args=' + args])
-            check_call(['ninja', '-C', build, lib])
+            try:
+                check_call(['ninja', '-C', build, lib])
+            except subprocess.CalledProcessError:
+                check_call(['ninja', '-C', build, '-t', 'clean'])
+                check_call(['ninja', '-C', build, lib])
             dst = '%s/%s' % (lib_dir, skia_to_android_arch_name_map[arch])
             makedirs(dst)
             shutil.copy(os.path.join(build, lib), dst)
diff --git a/tools/skqp/generate_gn_args b/tools/skqp/generate_gn_args
deleted file mode 100755
index ad1db01..0000000
--- a/tools/skqp/generate_gn_args
+++ /dev/null
@@ -1,46 +0,0 @@
-#! /usr/bin/env python
-
-# Copyright 2018 Google Inc.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import argparse
-import os
-import sys
-
-import skqp_gn_args
-
-def parse_args():
-    parser = argparse.ArgumentParser(description='Generate args.gn file.')
-    parser.add_argument('target_build_dir')
-    parser.add_argument('android_ndk_dir' )
-    parser.add_argument('--arch',  metavar='architecture', default='arm',
-        help='defaults to "arm", valid values: "arm" "arm64" "x86" "x64"')
-    parser.add_argument('--api_level', type=int, metavar='api_level',
-        default=26, help='android API level, defaults to 26')
-    parser.add_argument('--debug', default=False, action='store_true',
-        help='compile native code in debug mode, defaults to false')
-    args = parser.parse_args()
-    result = skqp_gn_args.GetGNArgs(args.arch,
-                                    os.path.abspath(args.android_ndk_dir),
-                                    args.debug,
-                                    args.api_level)
-    return args.target_build_dir, result)
-
-def write_gn(o, args):
-    l = max(len(k) for k in args)
-    for k, v in sorted(args.items()):
-        o.write('%-*s = %s\n' % (l, k, v))
-
-def make_args_gn(out_dir, args):
-    if out_dir == '-':
-        write_gn(sys.stdout, args)
-        return
-    if not os.path.exists(out_dir):
-        os.makedirs(out_dir)
-    with open(os.path.join(out_dir, 'args.gn'), 'w') as o:
-        write_gn(o, args)
-
-if __name__ == '__main__':
-    build_dir, args = parse_args()
-    make_args_gn(build_dir, args)
diff --git a/tools/skqp/gn_to_bp.py b/tools/skqp/gn_to_bp.py
index 8fd614a..99b022c 100755
--- a/tools/skqp/gn_to_bp.py
+++ b/tools/skqp/gn_to_bp.py
@@ -149,6 +149,9 @@
 defines.update(["SK_ENABLE_DUMP_GPU", "SK_BUILD_FOR_SKQP"])
 cflags_cc.update(['-Wno-extra-semi-stmt'])
 
+# Android does not want -Weverything set, it blocks toolchain updates.
+assert '-Weverything' not in cflags
+
 gn_to_bp_utils.GrabDependentValues(js, '//:libskqp_app', 'sources', srcs, None)
 gn_to_bp_utils.GrabDependentValues(js, '//:libskqp_app', 'include_dirs',
                                    local_includes, 'freetype')
diff --git a/tools/skqp/src/skqp.cpp b/tools/skqp/src/skqp.cpp
index 04f0369..ac08a5e 100644
--- a/tools/skqp/src/skqp.cpp
+++ b/tools/skqp/src/skqp.cpp
@@ -22,8 +22,12 @@
 #include "src/utils/SkOSPath.h"
 #include "tests/Test.h"
 #include "tools/fonts/TestFontMgr.h"
+#ifdef SK_GL
 #include "tools/gpu/gl/GLTestContext.h"
+#endif
+#ifdef SK_VULKAN
 #include "tools/gpu/vk/VkTestContext.h"
+#endif
 
 #include <limits.h>
 #include <algorithm>
diff --git a/tools/svg/svg_downloader.py b/tools/svg/svg_downloader.py
old mode 100644
new mode 100755
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
index 20f1a7d..1e3a3e8 100644
--- a/tools/valgrind.supp
+++ b/tools/valgrind.supp
@@ -282,9 +282,9 @@
     fun:_ZN12_GLOBAL__N_120ConvolveHorizontallyILb0EEEvPKhRK21SkConvolutionFilter1DPh
     fun:_Z14BGRAConvolve2DPKhibRK21SkConvolutionFilter1DS3_iPhRK18SkConvolutionProcsb
 }
-#Something odd happening in with SkRasterPipeline in GrConvertPixels. Seems bogus after
-#investigation. MSAN/ASAN have no complaints. Complaining about conditional jump or use
-#of var that is "uninitialized" but it definitely is.
+#Something odd is happening in SkRasterPipeline when called by GrConvertPixels and GrClearImage.
+#It seems bogus after investigation. MSAN/ASAN have no complaints. It's complaining about
+#conditional jump or use of var that is "uninitialized" but it definitely is.
 {
    grconvertpixels_rasterpipeline
    Memcheck:Cond
@@ -301,3 +301,19 @@
    fun:_Z15GrConvertPixelsRK11GrImageInfoPvmS1_PKvmb
    ...
 }
+{
+   grclearimage_rasterpipeline
+   Memcheck:Cond
+   ...
+   fun:_ZNK16SkRasterPipeline3runEmmmm
+   fun:_Z12GrClearImageRK11GrImageInfoPvm8SkRGBA4fIL11SkAlphaType3EE
+   ...
+}
+{
+   grclearimage_rasterpipeline
+   Memcheck:Value8
+   ...
+   fun:_ZNK16SkRasterPipeline3runEmmmm
+   fun:_Z12GrClearImageRK11GrImageInfoPvm8SkRGBA4fIL11SkAlphaType3EE
+   ...
+}
diff --git a/tools/viewer/ParticlesSlide.cpp b/tools/viewer/ParticlesSlide.cpp
index 4ea33d1..71a4c5b 100644
--- a/tools/viewer/ParticlesSlide.cpp
+++ b/tools/viewer/ParticlesSlide.cpp
@@ -10,6 +10,7 @@
 #include "modules/particles/include/SkParticleEffect.h"
 #include "modules/particles/include/SkParticleSerialization.h"
 #include "modules/particles/include/SkReflected.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/core/SkOSFile.h"
 #include "src/sksl/SkSLByteCode.h"
 #include "src/utils/SkOSPath.h"
@@ -207,6 +208,7 @@
     SkParticleEffect::RegisterParticleTypes();
     fName = "Particles";
     fPlayPosition.set(200.0f, 200.0f);
+    fResourceProvider = skresources::FileResourceProvider::Make(GetResourcePath());
 }
 
 void ParticlesSlide::loadEffects(const char* dirname) {
@@ -280,7 +282,8 @@
         for (int i = 0; i < fLoaded.count(); ++i) {
             ImGui::PushID(i);
             if (fAnimated && ImGui::Button("Play")) {
-                sk_sp<SkParticleEffect> effect(new SkParticleEffect(fLoaded[i].fParams, fRandom));
+                sk_sp<SkParticleEffect> effect(new SkParticleEffect(fLoaded[i].fParams,
+                                                                    fResourceProvider, fRandom));
                 effect->start(fAnimationTime, looped);
                 fRunning.push_back({ fPlayPosition, fLoaded[i].fName, effect, false });
                 fRandom.nextU();
diff --git a/tools/viewer/ParticlesSlide.h b/tools/viewer/ParticlesSlide.h
index 9d98d41..cf2e563 100644
--- a/tools/viewer/ParticlesSlide.h
+++ b/tools/viewer/ParticlesSlide.h
@@ -17,6 +17,8 @@
 class SkParticleEffect;
 class SkParticleEffectParams;
 
+namespace skresources { class ResourceProvider; }
+
 class ParticlesSlide : public Slide {
 public:
     ParticlesSlide();
@@ -52,6 +54,8 @@
         bool fTrackMouse;
     };
     SkTArray<RunningEffect> fRunning;
+
+    sk_sp<skresources::ResourceProvider> fResourceProvider;
 };
 
 #endif
diff --git a/tools/viewer/SkottieSlide.cpp b/tools/viewer/SkottieSlide.cpp
index ffa3321..5ddc2f4 100644
--- a/tools/viewer/SkottieSlide.cpp
+++ b/tools/viewer/SkottieSlide.cpp
@@ -12,7 +12,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkFont.h"
 #include "modules/skottie/include/Skottie.h"
-#include "modules/skottie/utils/SkottieUtils.h"
+#include "modules/skresources/include/SkResources.h"
 #include "src/utils/SkOSPath.h"
 #include "tools/timer/TimeUtils.h"
 
@@ -97,8 +97,8 @@
     fAnimation      = builder
             .setLogger(logger)
             .setResourceProvider(
-                skottie_utils::DataURIResourceProviderProxy::Make(
-                    skottie_utils::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str()),
+                skresources::DataURIResourceProviderProxy::Make(
+                    skresources::FileResourceProvider::Make(SkOSPath::Dirname(fPath.c_str()),
                                                               /*predecode=*/true),
                     /*predecode=*/true))
             .makeFromFile(fPath.c_str());
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index f02f5b9..ec9d523 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -129,6 +129,8 @@
                "Run threadsafe tests on a threadpool with this many extra threads, "
                "defaulting to one extra thread per core.");
 
+static DEFINE_bool(redraw, false, "Toggle continuous redraw.");
+
 
 const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
     "OpenGL",
@@ -311,6 +313,7 @@
     displayParams.fGrContextOptions.fSuppressPrints = true;
     displayParams.fGrContextOptions.fInternalMultisampleCount = FLAGS_internalSamples;
     fWindow->setRequestedDisplayParams(displayParams);
+    fRefresh = FLAGS_redraw;
 
     // Configure timers
     fStatsLayer.setActive(false);